【题解】Killer Names($O(n\log n)$做法)

【题解】Killer Names(\(O(n\log n)\)做法)

HDU - 6143

感觉好久没做过这种直来直去的组合题,过来水一篇题解。还以为要写一个\(MTT\)或者三模数\(NTT\),想了想HDU这种老年机子还是算了,最后发现\(O(n^2)\)就行了

题意翻译过后就是要求一个式子:

\[\sum_{i+j\le m}{m\choose i}{m-i\choose j}{n\brace i}{n\brace j}i!j! \]

钦定两边分别出现\(i,j({m\choose i}{m-i\choose j})\)种不同的颜色,然后再随意分布\(({n\brace i}{n\brace j}i!j!)\)

感觉这很可以卷积一下,但是我们这里是小于号咋办

假设大家都会母函数和NTT和卷积,

\[g_i=\sum_{i+j= m}{m\choose i}{m-i\choose j}{n\brace i}{n\brace j}i!j! \\ G(x)=\sum_{i=0}^mg_ix^i \]

那么这个式子可以化成

\[G(x){(1+x+x^2+x^3\dots)}=G(x){1\over 1-x} \]

就构造了一个小于号出来。好我们来拆这个式子

拆出来后是这样的

\[\sum_{i+j\le m}{m!\over (m-i-j)!}{n\brace i}{n\brace j} \]

你以为不能拆?但是!!!!!!别忘了\(g_i\)是等于号啊,所以\(i+j=m\),所以\((m-i-j)!=1\),因此继续化简,这样我们就可以卷积了。

\[\dfrac {g_i} {m!}=\sum_{i+j=m}{n\choose i}{n\choose j} \]

我们不需要多项式球逆,只需要\(O(n)\)构造\(1\over 1-x\)就行,NTT求一行斯特林数也是\(O(n\log n)\)所以复杂度\(O(n\log 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\n",ans);
      }
      return 0;
}


posted @ 2019-08-25 08:39  谁是鸽王  阅读(225)  评论(1编辑  收藏  举报