BZOJ1129: [POI2008]Per

模数m不是质数很麻烦qwq
先把他分解成 m=Mi=1piki m = ∏ i = 1 M p i k i
用每个 piki p i k i 做模数计算最后EXCRT合并

那么现在模数 Mod=piki M o d = p i k i
计算s的排名,按位枚举i,计算1~i-1位与s相同,第i位< s的序列数,最后+1
令p[i]表示位置i在序列里排第p[i]大,c[i]表示i~n中,序列第i大的数目,那么对于i,有 ans=p[i]1j=1(ni)!(c[j]1)!nk=1[k!=j]c[k]! a n s = ∑ j = 1 p [ i ] − 1 ( n − i ) ! ( c [ j ] − 1 ) ! ∏ k = 1 n [ k ! = j ] c [ k ] !

上下同乘c[j],有 ans=(ni)!nk=1c[k]!p[i]1j=1c[j] a n s = ( n − i ) ! ∏ k = 1 n c [ k ] ! ∑ j = 1 p [ i ] − 1 c [ j ]
于是用树状数组维护一下,i扫过去顺便维护前面的常数就行了

注意因为模数是 piki p i k i ,维护计算时要把 pi p i 这个因子单独提出来计算最后再乘回去,其他正常用欧拉定理算逆元

code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
int exgcd(int a,int b,int &x,int &y)
{
    if(!a)
    {
        x=0,y=1; return b;
    }
    int tx,ty,d=exgcd(b%a,a,tx,ty);
    x=ty-b/a*tx;
    y=tx;
    return d;
}
int pw(int x,int k,int Mod)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%Mod) if(k&1)
        re=(ll)re*x%Mod;
    return re;
}
const int maxn = 310000;

int n,m,mod,Mod,modk,u;
struct node{int x,i;}a[maxn];
inline bool cmpx(const node x,const node y){return x.xinline bool cmpi(const node x,const node y){return x.iint inv[maxn];
void pre()
{
    inv[1]=1; int phi=Mod/mod*(mod-1);
    for(int i=2;i<=n;i++) if(i%mod) inv[i]=pw(i,phi-1,Mod);
}

int num[maxn],s[maxn];
void add(int x,int c){for(;x<=u;x+=lowbit(x))s[x]+=c; return;}
int query(int x){int re=0;for(;x;x-=lowbit(x))re+=s[x];return re;}

int nowki,now;
int solve()
{
    int re=0; pre();

    now=1; nowki=0;
    for(int i=1;i<=n;i++) 
    {
        num[a[i].x]++;
        int j=i; while(j%mod==0) nowki++,j/=mod;
        now=(ll)now*j%Mod;
    }
    for(int i=1;i<=u;i++) 
    {
        add(i,num[i]);
        for(int j=1;j<=num[i];j++)
        {
            int k=j; while(k%mod==0) k/=mod,nowki--;
            now=(ll)now*inv[k]%Mod;
        }
    }

    for(int i=1;i<=n;i++)
    {
        int k=n-i+1; while(k%mod==0) k/=mod,nowki--;
        now=(ll)now*inv[k]%Mod;

        int tk=nowki<0?pw(mod,-nowki,3*n):pw(mod,nowki,Mod);
        int qt=query(a[i].x-1);
        int temp=nowki<0?(ll)qt/tk*now%Mod:(ll)qt*now%Mod*tk%Mod;
        re+=temp; if(re>=Mod) re-=Mod;

        k=num[a[i].x]; while(k%mod==0) k/=mod,nowki++;
        now=(ll)now*k%Mod;
        num[a[i].x]--; add(a[i].x,-1);
    }
    return re;
}

int ans[maxn],M[maxn],ki[maxn],tp;
int EXCRT()
{
    int qm=sqrt(m)+1; tp=0;
    for(int i=2;i<=qm&&m!=1;i++) if(m%i==0)
    {
        M[++tp]=i;
        while(m%i==0) ki[tp]++,m/=i;
    }
    if(m>1) M[++tp]=m,ki[tp]=1;

    int re,rem;
    for(int i=1;i<=tp;i++)
    {
        mod=M[i],modk=ki[i],Mod=1;
        for(int j=1;j<=modk;j++) Mod*=mod;
        int tmp=solve();
        if(i==1) re=tmp,rem=Mod;
        else
        {
            int tx,ty; exgcd(rem,Mod,tx,ty);
            Mod*=rem;
            re=((ll)tx*rem*(tmp-re)+re)%Mod;
            if(re<0) re+=Mod; rem=Mod;
        }
    }
    return (re+1)%Mod;
}

int main()
{
    read(n); read(m);
    for(int i=1;i<=n;i++) read(a[i].x),a[i].i=i;
    sort(a+1,a+n+1,cmpx);
    for(int i=1,las=0;i<=n;i++) 
    {
        if(las!=a[i].x) ++u;
        las=a[i].x; a[i].x=u;
    }
    sort(a+1,a+n+1,cmpi);

    printf("%d\n",EXCRT());

    return 0;
}

你可能感兴趣的:(Poi,BZOJ,数论,中国剩余定理)