版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 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种排名的方案数,答案为
求f考虑容斥,我们枚举多少个排名没人,于是
可以发现这个东西和斯特林数非常像。我们拆一下就能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;
}