牛客 二分图染色(弱化版)

链接:https://ac.nowcoder.com/acm/problem/13229
来源:牛客网

题目:二分图染色(弱化版)

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
给定一个完全二分图,图的左右两边的顶点数目相同。我们要给图中的每条边染成红色、蓝色、或者绿色,并使得任意两条红边不共享端点、同时任意两条蓝边也不共享端点。
计算所有满足条件的染色的方案数,并对10^9+7取模。
(ps:本题数据量与实际比赛中数据量相比,少了一些)
输入描述:
二分图单边的顶点数目n(n ≤ 10^7)
输出描述:
输出一个整数,即所求的答案。
示例1
输入
复制
2
输出
复制
35

首先我们先了解一下什么是完全二分图

完全二分图是一种特殊的二分图,可以把图中的顶点分成两个集合,使得第一个集合中的所有顶点都与第二个集合中的所有顶点相连。
如图1-1
牛客 二分图染色(弱化版)_第1张图片
详细概念请参考:https://baike.sogou.com/v68878629.htm?fromTitle=%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%88%86%E5%9B%BE

二分图染色(弱化版)题解
乍看一下,此题貌似很简单,仔细一想,竟然完全不可做。。。

然后,开始思考怎么搞这道题。。。

首先,我们因为每个边都要染色,所以,我们不妨先给所有边都染上最没影响的颜色——绿色

然后,我们只需考虑,将绿色的边改成红色或者蓝色即可~

我们来推导一下

如果既有蓝色,又有红色,尝试推导一下,发现情况太多,而且难以递推

那么,我们先从简单入手

假设,现在我们只有红色或者只有蓝色(以下假设连红边),此时答案是多少呢?

考虑递推

设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=∑nk=0(−1)k(Ckn)2i!f(n−k)2

我们直接递推组合数,再预处理出f数组就可以直接算了!

代码:

#include
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;
}

你可能感兴趣的:(ACM)