【题解】Killer Names((O(nlog n))做法)
感觉好久没做过这种直来直去的组合题,过来水一篇题解。还以为要写一个(MTT)或者三模数(NTT),想了想HDU这种老年机子还是算了,最后发现(O(n^2))就行了
题意翻译过后就是要求一个式子:
[sum_{i+jle m}{mchoose i}{m-ichoose j}{nrace i}{nrace j}i!j!
]
钦定两边分别出现(i,j({mchoose i}{m-ichoose j}))种不同的颜色,然后再随意分布(({nrace i}{nrace j}i!j!))
感觉这很可以卷积一下,但是我们这里是小于号咋办
假设大家都会母函数和NTT和卷积,
设
[g_i=sum_{i+j= m}{mchoose i}{m-ichoose j}{nrace i}{nrace j}i!j!
\
G(x)=sum_{i=0}^mg_ix^i
]
那么这个式子可以化成
[G(x){(1+x+x^2+x^3dots)}=G(x){1over 1-x}
]
就构造了一个小于号出来。好我们来拆这个式子
拆出来后是这样的
[sum_{i+jle m}{m!over (m-i-j)!}{nrace i}{nrace j}
]
你以为不能拆?但是!!!!!!别忘了(g_i)是等于号啊,所以(i+j=m),所以((m-i-j)!=1),因此继续化简,这样我们就可以卷积了。
[dfrac {g_i} {m!}=sum_{i+j=m}{nchoose i}{nchoose j}
]
我们不需要多项式球逆,只需要(O(n))构造(1over 1-x)就行,NTT求一行斯特林数也是(O(nlog n))所以复杂度(O(nlog n))
而且还可以加强一下,比如两边名字长度不同之类的...必须用至少(k)个不同的字母之类的...
但是这里模数居然不是清真的(998244353),所以你得写三模数NTT再CRT合并!!那对不起我赶时间还是写(O(n^2))算这个式子...
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=2e3+1;
const int mod=1e9+7;
int s[maxn][maxn];
int jc[maxn],inv[maxn];
inline int ksm(const int&ba,const int&p){
register int ret=1;
for(register int t=p,b=ba%mod;t;t>>=1,b=1ll*b*b%mod) if(t&1) ret=1ll*ret*b%mod;
return ret;
}
inline void pre(const int&n){
s[0][0]=1;
for(register int t=1;t<=n;++t)
for(register int i=1;i<=n;++i)
s[t][i]=(1ll*s[t-1][i]*i%mod+s[t-1][i-1])%mod;
inv[0]=jc[0]=1;
for(register int t=1;t<=n;++t) jc[t]=1ll*jc[t-1]*t%mod;
inv[n]=ksm(jc[n],mod-2);
for(register int t=n-1;t;--t) inv[t]=1ll*inv[t+1]*(t+1)%mod;
}
inline int c(const int&n,const int&m){
if(n<m) return 0;
return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int n,m;
int main(){
pre(2e3);
int T=qr();
while(T--){
n=qr(); m=qr(); int ans=0,ed=min(n,m); int*S=s[n];
for(register int t=1;t<=ed;++t)
for(register int i=1;t+i<=m&&i<=n;++i)
ans=(ans+1ll*c(m,t)*c(m-t,i)%mod*S[t]%mod*S[i]%mod*jc[t]%mod*jc[i]%mod)%mod;
printf("%d
",ans);
}
return 0;
}