jzoj5848 Strange NTT

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/82026228

Description


期末考试结束了, 小 C 所在的班级要进行考试成绩的排名.
排名规则是这样的: 对于成绩为 ai 的同学, 他的排名等于成绩严格小于 ai 的同学的成绩 aj 组成的集合 {aj} 的大小.
现在小 C 想知道, 如果有 N 个人参加了考试, 一共有多少种可能的排名结果. 两种排名结果不同当且仅当至少有一个人在两次排名中排名不同.

输出一行一个整数, 表示答案对 1004535809 取模的结果.

对于 15% 的数据, N ≤ 5.
对于 30% 的数据, N ≤ 50.
对于 45% 的数据, N ≤ 500.
对于 60% 的数据, N ≤ 5000.
对于 100% 的数据, N ≤ 100000.

Solution


首先这个模数非常exciting,这提醒我们要在这上面搞事情

考虑dp,设f[i,j]表示i个人j种排名的方案数,那么有f[i,j]=(f[i-1,j]+f[i-1,j-1])*j,这样就能拿到60分的高分
考虑NTT,设f[n,m]表示n个人m种排名的方案数,答案为 a n s = i = 1 m f [ n , i ]
求f考虑容斥,我们枚举多少个排名没人,于是 f [ n , m ] = k = 0 m ( 1 ) k ( m k ) ( m k ) n
可以发现这个东西和斯特林数非常像。我们拆一下就能NTT了。如果线性筛n次幂还能更快一点

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st,_=ed;i<=_;++i)
#define drp(i,st,ed) for (register int i=st,_=ed;i>=_;--i)

typedef long long LL;
const int MOD=1004535809;
const int N=2097156;

LL fac[N],fny[N],pw[N],a[N],b[N],ny;

bool not_prime[N+5];

int rev[N],prime[N];

LL ksm(LL x,LL dep) {
    LL ret=1;
    for (;dep;dep>>=1) {
        (dep&1)?(ret=ret*x%MOD):0;
        x=x*x%MOD;
    }
    return ret;
}

void pre_work(int n) {
    pw[0]=0; pw[1]=1;
    rep(i,2,n) {
        if (!not_prime[i]) {
            pw[i]=ksm(i,n); prime[++prime[0]]=i;
        }
        for (int j=1;i*prime[j]<=n&&j<=prime[0];j++) {
            not_prime[i*prime[j]]=1;
            pw[i*prime[j]]=pw[i]*pw[prime[j]]%MOD;
            if (i%prime[j]==0) break;
        }
    }
}

void NTT(LL *a,int len,int f) {
    for (int i=1;i<len;i++) if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
    for (int i=1;i<len;i*=2) {
        LL wn=ksm(3,(MOD-1)/i/2);
        if (f==-1) wn=ksm(3,(MOD-1)-(MOD-1)/i/2);
        for (int j=0;j<len;j+=i*2) {
            LL w=1;
            for (int k=0;k<i;k++) {
                LL u=a[j+k],v=a[j+k+i]*w%MOD;
                a[j+k]=(u+v)%MOD; a[j+k+i]=(u-v+MOD)%MOD;
                w=w*wn%MOD;
            }
        }
    }
    if (f==-1) for (int i=1;i<len;i++) a[i]=a[i]*ny%MOD;
}

int main(void) {
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    int n,i; scanf("%d",&n); pre_work(n);
    fac[0]=1; rep(i,1,n) fac[i]=fac[i-1]*i%MOD;
    fny[0]=1; fny[n]=ksm(fac[n],MOD-2); drp(i,n-1,1) fny[i]=fny[i+1]*(i+1)%MOD;
    rep(i,0,n) a[i]=((i&1)?(-1):(1))*fny[i]%MOD;
    rep(i,0,n) b[i]=pw[i]*fny[i]%MOD;
    int len,lg; for (len=1,lg=0;len<=n*2;len*=2,lg++);
    ny=ksm(len,MOD-2);
    rep(i,0,len) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
    NTT(a,len,1); NTT(b,len,1);
    rep(i,0,len) a[i]*=b[i];
    NTT(a,len,-1);
    LL ans=0;
    rep(i,1,n) ans=(ans+a[i]*fac[i])%MOD;
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/82026228
NTT