zoukankan      html  css  js  c++  java
  • 【BZOJ】P1129 PER

    数论+组合计数

    题目链接

    一道鬼题!

    part1.不看取模,假设所有元素互不相等

    显然,这是一个康托展开。

    int cantor(int a[],int n){//cantor展开,n表示是n位的全排列,a[]表示全排列的数(用数组表示)
        int ans=0,sum=0;
        for(int i=1;i<n;i++){
            for(int j=i+1;j<=n;j++)
                if(a[j]<a[i])
                    sum++;
            ans+=sum*factorial[n-i];//累积
            sum=0;//计数器归零
        }
        return ans+1;
    }
    

    由于(nle 300000),所以求有多少个数小于(a[i])要用树状数组优化。


    part2.不看取模,所有元素可以有一部分相等

    考虑一个元素x出现了(cnt[x])次,那么其对答案的贡献就是(cnt[x]!),所以当前第i位的贡献就是

    [frac{sum cdot (n-i)!}{prod cnt[j]!} ]

    注意:我们要先把(cnt[A[i]]++)再计算。

    请看栗子:

    序列: 2 4 2 1

    算第1位时,发现有1个数1比(a[1])小,因此在此之前有1开头的。

    即1 2 2 4 ,1 2 4 2,1 4 2 2,个数为(frac{1 cdot 3!}{2!}),但如果不先把(cnt[2]++)的话,计算时(cnt[2]=1),显然不行

    那么这部分也完美解决了。


    part3.取模,所有元素可以有一部分相等

    你可能会认为,取模,不就是搞个模逆元就行了嘛。

    错了,这里的模数m,并不是我们常用的1e9+7,也就是说其不一定是个质数,那么显然费小是行不通的。

    那咋办嘛?

    观察一下这个式子:

    [frac{sum cdot (n-i)!}{prod cnt[j]!} ]

    显然,这是个整数。那么,我们将分母和分子进行质因子分解,对于每个质因子,肯定在分母中的数量大于等于分子中的数量。

    那么,我们先提出m的质因子,再在该式子的分母和分子中提出m中含有的质因子。

    也就是说,我们把式子拆成两部分:

    [Xcdot frac{A}{B} ]

    其中,A和B不含m的质因子。

    这样,提完后分子就与m互质了,然后就可以愉快地模逆元了~~

    注意:m不一定是质数,因此还得用拓欧去求

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define int long long
    #define MAXN 1000010
    using namespace std;
    ll n,m,A[MAXN],BIT[MAXN],cnt[MAXN],p[101],CntMum[101],CntSon[101],maxn;
    ll mum=1,son=1,ans;
    inline void Add(ll i,ll x){
        while(i<=maxn)BIT[i]+=x,i+=i&-i;
    }
    inline ll Query(ll i){
        ll res=0;
        while(i>=1)res+=BIT[i],i-=i&-i;
        return res;
    }
    inline void updateSon(ll x){
        if(!x)return;
        for(register int i=1;i<=p[0];++i){
            while(x%p[i]==0){
                CntSon[i]++;
                x/=p[i];
            }
        }
        son=son*1LL*x%m;
    }
    inline void updateMum(ll x){
        if(!x)return;
        for(register int i=1;i<=p[0];++i){
            while(x%p[i]==0){
                CntMum[i]++;
                x/=p[i];
            }
        }
        mum=mum*1LL*x%m;
    }
    inline ll update2(ll x,ll pos){
        if(!x)return 0;
        for(register int i=1;i<=p[0];++i){
            while(x%p[i]==0){
                CntMum[i]+=pos;
                x/=p[i];
            }
        }
        return x;
    }
    inline ll Quick_Pow(ll a,ll p){
        ll res=1;
        while(p){
            if(p&1)res=res*a%m;
            a=a*a%m;
            p>>=1;
        }
        return res;
    }
    inline void exgcd(ll a,ll b,ll &x,ll &y){
        if(b==0){
            x=1,y=0;
            return;
        }
        exgcd(b,a%b,y,x);
        y=(y-(a/b)*x%m+m)%m;
    }
    signed main(){
        scanf("%lld %lld",&n,&m);
        for(register int i=1;i<=n;i++)scanf("%lld",&A[i]),maxn=max(maxn,A[i]);
        int x=m;
        for(register int i=2;i*i<=x;i++){
            if(x%i==0)p[++p[0]]=i;
            while(x%i==0)x/=i;
        }
        if(x!=1)p[++p[0]]=x;
        ll Ans=1,X,Y;
        for(register int i=n;i>=1;--i){
            Ans=1;
            ++cnt[A[i]];
            updateSon(cnt[A[i]]);
            if(n!=i)updateMum(n-i);
            Add(A[i],1);
            register ll num=Query(A[i]-1),tmp;
            if(num==0)continue;
            tmp=update2(num,1);
            for(register int j=1;j<=p[0];++j)Ans=Ans*Quick_Pow(p[j],CntMum[j]-CntSon[j])%m;
            exgcd(son,m,X,Y);
            Ans=Ans*(tmp*1LL*mum%m)%m*X%m;
            update2(num,-1);
            ans+=Ans;
            (ans>=m)&&(ans-=m);
        }
        cout<<(ans+1)%m;
        return 0;
    }
    
  • 相关阅读:
    一步一步做高性能服务器(C++) -- Day 3
    一步一步做高性能服务器(C++) -- Day 2
    一步一步做高性能服务器(C++) -- Day 1
    一步一步做高性能服务器(C++) -- Day 0
    HDU ACMSTEPS 1.3.4
    铭记
    子窗口与主窗口分开处理
    autoit小贴士
    autoit使用WMIC获取硬件信息
    Windows WMIC命令使用详解
  • 原文地址:https://www.cnblogs.com/SillyTieT/p/11503482.html
Copyright © 2011-2022 走看看