BZOJ1129: [POI2008]Per

77 篇文章 0 订阅
42 篇文章 0 订阅

模数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<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#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.x<y.x;}
inline bool cmpi(const node x,const node y){return x.i<y.i;}

int 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值