题解:P13234 [GCJ 2015 Finals] Campinatorics

· · 题解

小清新数数题,像是二维错排。

考虑只有 $1,2$ 怎么做,设答案为 $f_n$。 一个平凡的想法是从 $f_{n-1}$ 转移而来。我们钦定第 $n$ 行的第 $n$ 个数为 $1$,然后枚举第 $n$ 行 $2$ 的位置 $i$,然后将原本第 $i$ 列的 $2$ 放置到第 $n$ 列,这样我们就实现了一个 $f_{n-1}\rightarrow f_n$ 的映射,即 $$f_n=n\times (n-1)\times f_{n-1}$$ 乘的 $n$ 是把钦定的最后一行放回到任意一行中。 然而这样存在漏数的情况。类比错拍的递推,上式默认了去掉特定的一行一列(第 $n$ 列有 $1$ 的行,该行有 $2$ 的列)后的矩阵依然合法,而则不总是存在。 考虑计算不存在的情况。容易发现,不存在当且仅当该特定的行列处于一个合法的 $2\times 2$ 矩阵中(这也和错排类似),这一部分的贡献是 $n\times (n-1)^2\times f_{n-2}$。 最后再处理 $3$ 的影响即可。 时间复杂度 $O(Tn)$。 ```cpp #include<bits/stdc++.h> #define il inline using namespace std; const int N=1000000; const int maxn=1000010; const int mod=1e9+7; il int read(){ int x=0; char c=getchar(); for(;!(c>='0'&&c<='9');c=getchar()); for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+c-'0'; return x; } il int fpow(int n,int m,int x=1){ for(;m;m>>=1,n=1ll*n*n%mod) if(m&1) x=1ll*x*n%mod; return x; } int fac[maxn],ifac[maxn]; int f[maxn],T,n,X,ans; int C(int i){return (1ll*i*(i-1)/2)%mod;} int main(){ fac[0]=ifac[0]=1; for(int i=1;i<=N;i++) fac[i]=1ll*fac[i-1]*i%mod; ifac[N]=fpow(fac[N],mod-2); for(int i=N-1;i;i--) ifac[i]=1ll*ifac[i+1]*(i+1)%mod; f[0]=1,f[1]=0,f[2]=2; for(int i=3;i<=N;i++){ f[i]=1ll*f[i-1]*(i-1)%mod*i%mod; f[i]=(f[i]+1ll*f[i-2]*i%mod*(i-1)%mod*(i-1))%mod; } // for(int i=1;i<=10;i++) // printf("%d ",f[i]); // printf("\n"); T=read(); for(int Case=1;Case<=T;Case++){ n=read(),X=read(),ans=0; for(int i=1,tot=1;i<=n;i++){ tot=1ll*tot*(n-i+1)%mod*(n-i+1)%mod; if(i>=X) ans=(ans+1ll*tot*f[n-i]%mod*ifac[i])%mod; } if(!X) ans=(ans+f[n])%mod; printf("Case #%d: %d\n",Case,ans); } return 0; } ```