二分图染色(弱化版)题解
乍看一下,此题貌似很简单,仔细一想,竟然完全不可做。。。
然后,开始思考怎么搞这道题。。。
首先,我们因为每个边都要染色,所以,我们不妨先给所有边都染上最没影响的颜色——绿色
然后,我们只需考虑,将绿色的边改成红色或者蓝色即可~
我们来推导一下
如果既有蓝色,又有红色,尝试推导一下,发现情况太多,而且难以递推
那么,我们先从简单入手
假设,现在我们只有红色或者只有蓝色(以下假设连红边),此时答案是多少呢?
考虑递推
设\(fi\)表示有i对点的完全二分图,然后,我们只有红色以及绿色涂料的时候,方案数是多少
可以轻松手算出来,\(f0=1,f1=2\)
然后,假设我们算出了所有\(f1->f(i-1)\),这时怎么算\(fi\),我们画个图,发现,我们现在就相当于在i-1的图下面加了一对点,那么,我们分类讨论这对点的连边情况
1.这对点只连绿边
方案数为\(f(i-1)\)
2.这对点之间连了一条红边
发现这对点现在只能连绿边了,所以方案数为\(f(i-1)\)
3.这对点之间连绿边,且至少一个点连了红边
我们来想下,这对边中的一个点,与之前的一个点之前连了一条红边,那么,其实就相当于还剩i-1对待连边的点(刚刚连边的点已经无法连其他红边了),而两个点分别对应的点仍然可以连边,所以方案数就是:
\(2*(i-1)[两个点选一个,i-1对点选一对]*f(i-1)\)
但是我们发现,对于左边和右边都连了红边的情况,我们计算了两次,所以我们还有减去两边都连了红边(不相连)的情况,即减去
\((i-1)^2[各选(i-1)对点]*f(i-2)\)
所以,\(fi=2*i*f(i-1)-(i-1)^2*f(i-2)\)
然后,我们再考虑有蓝边的情况
我们发现直接用容斥就可以算出答案了
即:\(ans=\sum_{k=0}^{n}\)\((−1)^{k}\)\((C_{n}^{k})^2\)\(i!\)\(f(n−k)^2\)
我们直接递推组合数,再预处理出\(f\)数组就可以直接算了!
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+1,mod=1e9+7;
int f[N],s[N],g[N];
int main(){
int n;
scanf("%d",&n);
f[0]=1,f[1]=2;
for(int i=2;i<=n;++i){
f[i]=(((2LL*i*f[i-1])%mod)-(1LL*((1LL*(i-1)*(i-1))%mod)*f[i-2]))%mod;
}
for(int i=1;i<=n;++i){
f[i]=(1LL*f[i]*f[i])%mod;
}
s[0]=s[1]=1;
for(int i=2;i<=n;++i){
s[i]=(1LL*s[i-1]*i)%mod;
}
g[0]=g[1]=1;
int ans=0,C=n;bool now=0;
for(int i=2;i<=n;++i){
now^=1;g[i]=(1LL*g[mod%i]*(mod-mod/i))%mod;
C=(1LL*C*(n-i+1))%mod,C=(1LL*C*g[i])%mod;
int fow=(1LL*((1LL*((1LL*C*C)%mod)*s[i])%mod)*f[n-i])%mod;
if(now){
ans=(ans+fow)%mod;
}else{
ans=(ans-fow)%mod;
}
}
ans=(ans+f[n])%mod;
ans=(ans-(1LL*((1LL*n*n)%mod)*f[n-1]))%mod;
ans=(ans+mod)%mod;
printf("%d",ans);
return 0;
}