题目传送门
分析:
由于红蓝有限制,我们可以先考虑染红色和蓝色,然后用绿色补完整个图
常常有把棋盘转化为二分图的套路,这次是把二分图转化为棋盘
那么红色和蓝色在二分图上没有公共点,相当于在棋盘上放任意多的车,使得车不能互相攻击到
假设大小为\(n\)的棋盘的方案数为\(F_n\),我们可以枚举放多少个车:
\(F_n=\sum_{i=0}^{n}C_{n}^{i}A_{n}^{i}\)
(这个不用解释了吧2333)
两种颜色的话我们不能直接用\(F_n^2\),这样会重复计算两种颜色的车在同一个位置的方案
考虑容斥原理,至少有\(i\)个两种颜色的车在同一位置:
\(Ans=\sum_{i=0}^{n}C_n^iA_n^iF_{n-i}^2\)
这样是\(O(n^2)\)的,我们考虑如何优化,尝试求出\(F\)的递推式
从\(F_{n-1}\)转移到\(F_n\)我们加了一行一列\(2n-1\)个格子都可以放车,也可以不放,总共\(2n\)种
可能会与前面\(n-1\)行或列的某一行或列矛盾,剩下\(n-2\)列随便放,综合起来得到递推式:
\(F_n=2nF_{n-1}-(n-1)^2F_{n-2}\)
于是就\(O(n)\)做了
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<iostream>
#include<map>
#include<string>
#include<bitset>
#define maxn 10000005
#define MOD 1000000007
using namespace std;
inline long long getint()
{
long long num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n;
int fac[maxn],inv[maxn],f[maxn];
inline int C(int p,int q)
{return 1ll*fac[p]*inv[q]%MOD*inv[p-q]%MOD;}
inline int A(int p,int q)
{return 1ll*fac[p]*inv[p-q]%MOD;}
int main()
{
n=getint();
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)fac[i]=1ll*fac[i-1]*i%MOD;
for(int i=2;i<=n;i++)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=2;i<=n;i++)inv[i]=1ll*inv[i]*inv[i-1]%MOD;
f[0]=1,f[1]=2;
for(int i=2;i<=n;i++)f[i]=(2ll*i*f[i-1]%MOD-1ll*(i-1)*(i-1)%MOD*f[i-2]%MOD+MOD)%MOD;
int ans=0;
for(int i=0;i<=n;i++)
{
int tmp=1ll*C(n,i)*A(n,i)%MOD*f[n-i]%MOD*f[n-i]%MOD;
if(i&1)ans=(ans-tmp+MOD)%MOD;
else ans=(ans+tmp)%MOD;
}
printf("%d\n",ans);
}