ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

解题报告——P3477 [POI2008]PER-Permutation

2022-08-19 09:33:33  阅读:108  来源: 互联网

标签:tmp cnt POI2008 int ans PER P3477 tmpans 100


这道题如果不是任意模数的话还是比较平凡的(

这道题的式子其实很好推,根据康托展开的思路,一位一位考虑,只不过是多重集,可能有重复情况,排除即可,每一位的式子为:

\[ans_i=\dfrac{(n-i)!}{\prod cnt_j}\times\sum^n_{k=i+1}[ a_k<a_i] \]

(\(cnt_j\) 为 \(i-n\) 之间 \(j\) 的个数,\(a_i\) 为第 \(i\) 位的值)

后面的和式为 \(i-n\) 之间值大于第 \(i\) 位的值的数量,同康托展开,用树状数组处理,接下来考虑如何处理任意模数。

\(cnt_j\) 中有可能会出现与 \(p\) 不互质的情况,这种条件下不存在逆元,所以可以借鉴扩展卢卡斯的思路,把分子和分母中 \(p\) 的质因子拆分出来,再来计算分母逆元,最后累计答案的时候再把 \(p\) 分离出来的质因子乘进去即可。

另外,对于每一位的答案肯定不能单独计算,否则时间复杂度会爆掉,所以考虑答案递推,首先从后向前推肯定相对简单,考虑每往前走一步对答案的影响,假设从 \(t+1\) 走到 \(t\) 分子,分子从 \((n-t-1)!\) 变为 \((n-t)!\) 乘上了 \(n-t\),\(cnt_{a_t}\) 增加了1,相当于乘上了 \(\dfrac{cnt_{a_t}-1}{cnt_{a_t}}\),把上面两式分离质因子求逆元乘进答案递推即可,初始状态 \(ans_n=0\)

Code:

#include<bits/stdc++.h>
#define N 300005ll
#define int long long
using namespace std;

int n,m,s[N],ans=1,buk[N],cnt[100];
int x,y,fac[100][N],base[100],tot;
struct Fenwick{
    int c[N];
    inline int lowbit(int X) {return X&(-X);}
    inline void update(int pos,int val){
        for(int i=pos;i<=N-5;i+=lowbit(i))
            c[i]+=val;
    }
    inline int sum(int pos){
        int temp=0;
        for(int i=pos;i;i-=lowbit(i))
            temp+=c[i];
        return temp;
    }
}T;
void exgcd(int a,int b);
int inv(int a,int p);
void factor(int a);
void change(int &a,int type);
int prod();

signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        scanf("%lld",s+i);
    factor(m);
    buk[s[n]]++;T.update(s[n],1);
    int tmpans=1,tmp;
    for(int i=n-1;i>=1;i--){
        T.update(s[i],1);
        change(tmp,1);
        tmpans=tmpans*tmp%m;
        tmp=++buk[s[i]];
        change(tmp,-1);
        tmpans=tmpans*inv(tmp,m)%m;
        ans=(ans+tmpans*T.sum(s[i]-1)%m*prod()%m)%m;
    }
    cout<<ans;
}

void exgcd(int a,int b){
    if(!b){
        x=1,y=0;
        return;
    }
    exgcd(b,a%b);
    int tmp=x;
    x=y,y=tmp-a/b*y;
}

int inv(int a,int p){
    exgcd(a,p);
    return (x%p+p)%p;
}

void factor(int a){
    for(int i=2;a!=1;i++)
        if(!(a%i)){
            base[++tot]=i;
            fac[tot][0]=1;
            while(!(a%i)) a/=i;
            for(int j=1;j<=N-5;j++)
                fac[tot][j]=fac[tot][j-1]*i%m;
        }
}

void change(int &a,int type){
    for(int i=1;i<=tot;i++)
        while(!(a%base[i])){
            a/=base[i];
            cnt[i]+=type;
        }
}

int prod(){
    int Tmp=1;
    for(int i=1;i<=tot;i++)
        Tmp=Tmp*fac[i][cnt[i]]%m;
    return Tmp;
}

标签:tmp,cnt,POI2008,int,ans,PER,P3477,tmpans,100
来源: https://www.cnblogs.com/Rolling-star/p/16600838.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有