【题解】Gnutella Chessmaster(容斥+下降幂+二项式定理)
绝世好题!
题目大意:有一个\(n\times n\)的棋盘,现在要在上面放\(k\)个Bishop,每个Bishop打两条对角线,问你放Bishop的方案数,方案不同当且仅当一个位置上存在主教的状态不同。你要对于每个k输出方案。
prop1
对于这个棋盘二分图染色((1,1)=白),显然白色格子上面的Bishop打不到黑色格子上面的Bishop,于是我们可以分开算黑白格子的方案数然后卷积起来。
prop2
我们把白色格子抠出来,然后旋转45度,问题转化为了在上面放中国象棋::车
,使得车互不攻击的方案数。
可以发现格子不是一个规整的形状,但是我们可以随意交换两行两列方案数不变。
我们考虑把他变成一个类似阶梯的东西,使得高的在右
然后我们考虑一个简单一点的问题
在\(n\)级阶梯上放\(k\)个
中国象棋::车
且他们互不攻击的方案书记为\(R_{k}^n\),那么
\[
R_{k}^n={n+1 \brace n-k+1}
\]
证明在最下面的补充里
可以发现,我们变出来的东西,只有最上面是少的,我的意思是,相邻阶梯高度差小于等于2,且不存在大于等于2的连续段,也就是说我们每次需要补的只是某级阶梯最上面那一个格子。补全之后有些地方是禁区,可以容斥。
algo
考虑容斥上面这个东西,考虑在一个禁区(一个阶梯的最上面那个方块)放一个中国象棋::车
的话,直接把一行一列消掉并且发现新的形状是一个子问题(仍然是一个阶梯,因为只是消掉了比他高的列,不影响比他小的列),所以考虑一下容斥,枚举放了几个禁区。
那么设\(a_k\)为原问题答案,总共有\(m\)个禁区,那么
\[
a_k=\sum_{i=0}^m (-1)^i{m\choose i}R_{k-i}^{n-i}
\]
代入
\[
a_k=\sum_{i=0}^m(-1)^i{m\choose i}{{n-i+1}\brace {n-k+1}}
\]
化开斯特林数
\[
a_k=\sum_{i=0}^m (-1)^i{m\choose i}\sum_{j=0}^{n-k} {n-k+1\choose j}{1\over (n-k+1)!}(-1)^j (n+1-k-j)^{n-i+1}
\]
最后那一项不好办,但是我们可以直接交换和式
\[
a_k=\sum_{j=0}^{n-k} (-1)^j {1\over j!(n+1-k-j)!}\sum_{i=0}^m (n+1-k-j)^{n-i+1}{m\choose i} (-1)^i
\]
二项式定理
\[
a_k=\sum_{j=0}^m (-1)^j {(n+1-k-j)^{n-m+1}\over j!(n+1-k-j)!}\times (n-k-j)^m
\]
还整理一下
\[
a_k=\sum_{j=0}^{n-k} {(-1)^j\over j!} \times {(n+1-k-j)^{n-m+1}\times (n-k-j)^m\over (n+1-k-j)!}
\]
这个看起来可以NTT了
答案算出来老不对 为什么...?????
一些补充
Rook Factorization定理
在\(n\)级阶梯上放\(k\)个
中国象棋::车
且,第\(i\)级阶梯高度为\(h_i\),则使得他们互不攻击的放置方案数记为\(R_{k}^n\),那么
\[
\sum_{i} R_{n-i}x^{\underline i} = \prod_i(x-h_i-i+1)
\]
根据组合意义相当于在阶梯下方补格子。代入\(h_i=i\)然后套用斯特林数就行
\(O(n^2)\)代码
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<deque>
#include<assert.h>
using namespace std; typedef long long ll;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1<<19;
const int mod=998244353;
const int g=3;
const int gi=(mod+1)/3;
int jc[maxn],inv[maxn];
int s[5001][5001];
typedef vector<int> poly;
inline int MOD(const int&x){return x>=mod?x-mod:x;}
inline int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
inline int MOD(const vector<int>&ve){int ret=1;for(const auto&t:ve) ret=MOD(t,ret); return ret;}
inline int ksm(const int&ba,const int&p){
int ret=1;
for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
if(t&1) ret=MOD(ret,b);
return ret;
}
void NTT(poly&a,const int&tag){
static int r[maxn];
int len=a.size();
for(int t=0;t<len;++t)
if((r[t]=r[t>>1]>>1|(t&1?len>>1:0))>t)
swap(a[t],a[r[t]]);
for(int t=1,wn,s=tag==1?g:gi;t<len;t<<=1){
wn=ksm(s,(mod-1)/(t<<1));
for(int i=0;i<len;i+=t<<1)
for(int j=0,p,w=1;j<t;++j,w=MOD(w,wn))
p=MOD(a[i+j+t],w),a[i+j+t]=MOD(a[i+j]-p+mod),a[i+j]=MOD(a[i+j]+p);
}
if(tag!=1)
for(int t=0,i=mod-(mod-1)/len;t<len;++t)
a[t]=MOD(a[t],i);
}
poly operator * (poly a,poly b){
int t1=a.size()+b.size()-1,len=1;
while(len<t1) len<<=1;
a.resize(len); b.resize(len);
NTT(a,1); NTT(b,1);
for(int t=0;t<len;++t) a[t]=MOD(a[t],b[t]);
NTT(a,-1); a.resize(t1);
return a;
}
void pre(const int&n){
s[0][0]=1;
for(int t=1;t<=5000;++t)
for(int i=1;i<=5000;++i)
s[t][i]=MOD(s[t-1][i-1]+MOD(i,s[t-1][i]));
//cerr<<s[3][2]<<' '<<s[3][1]<<' '<<s[5][1]<<endl;
jc[0]=inv[0]=1;
for(int t=1;t<=n;++t) jc[t]=MOD(jc[t-1],t);
inv[n]=ksm(jc[n],mod-2);
for(int t=n-1;t;--t) inv[t]=MOD(inv[t+1],t+1);
for(int t=1;t<=n;++t)
if(MOD(inv[t],jc[t])!=1)
puts("wa");
}
#define print(s) cerr<<#s" = ",printt(s)
void printt(poly a){
for(auto t:a) fprintf(stderr,"%d ",t);
cerr<<'\n';
}
int c(int n,int m){
if(n<m) return 0;
return MOD(jc[n],MOD(inv[m],inv[n-m]));
}
poly ans,wh,bl;
poly gen(poly st){
sort(st.begin(),st.end());
deque<int> q(st.begin(),st.end());
int n=1,m=0;
while(q.size())
if(q[0]==n) q.pop_front(),++n;
else if(q[0]==n-1) ++q[0],++m;
else if(q[0]<n-1) puts("wa");
else if(q[0]>n) q.push_front(n),++m,assert(n==1);
--n;
poly a(n+1),b(n+1),ret(n+1);
for(int k=1;k<=n;++k)
for(int i=0;i<=m;++i){
int temp=MOD(c(m,i),s[n-i+1][n-k+1]);
if(i&1) temp=mod-temp;
ret[k]=MOD(ret[k]+temp);
}
ret[0]=1;
return ret;
}
int main(){
int n=qr(); pre(1e5);
for(int t=1;t<=n+n-1;++t) (t&1?wh:bl).push_back(n-abs(n-t));
ans=gen(wh)*gen(bl);
for(int t=1;t<=n+n-1;++t)
printf("%d ",ans[t]);
putchar('\n');
return 0;
}
原文地址:https://www.cnblogs.com/winlere/p/12163259.html