• LOJ6160 美团 CodeM 初赛 Round A 二分图染色【容斥】


    题目链接

    题目解析

    考试的时候我是在按照点在想,可以把点分成四类:只有绿色边,有一条红色边,有一条蓝色边,有一条红色边和一条蓝色边(后三种其余都是绿色边),然后把(n)分解成四个整数的和,再分别计数。但是一个点的情况可能会影响到其它与之有边相连的点,所以不好做。

    不要怕,直接来

    还是从边的角度来考虑。

    由于绿色没有影响,所以我们把它当作不染色。先算只有一种颜色的染色方案。设(f(n))表示对(n)个点的二分图的染色方案,由于同一个点只能染一条边,先枚举一个有染色边的点数(i),然后就相当于从这(n)个点中选(i)个点出来,连到另一边的点去,而另一边的点也不能同时连两条有色边,所以另一边也要选(i)个点出来。注意到我一边选点的时候不在乎顺序,但是连到另一边的时候,另一边的点与这一边的点的对应情况不一样,那么连出来的边就不一样,所以另一边的点有顺序。

    得到(f(n)=sum_{i=0}^nC_n^iA_n^i)

    现在有两种颜色,如果直接算(f^2(n))会把一条边染上两种颜色,现在考虑一下怎么减去不合法的方案。可以用容斥原理,我们钦定一些边让它们被染上两种颜色,总方案数(=)钦定(0)条边被染两种颜色,其它随便(-)钦定(1)条边被染两种颜色,其它随便(+)钦定(2)条边被染两种颜色,其它随便(……)

    具体而言,(ans=sum_{i=0}^n (-1)^i*C(n,i)*A(n,i)*f^2(n-i))

    ((-1)^i)是容斥系数,(C(n,i)*A(n,i))是钦定(i)条边出来染两种颜色,原理同上,(*f^2(n-i))是剩下的边随便染色。

    组合数可以预处理,但是(f( i))(n^2)求,现在,我们就有了一个(n^2)的优秀算法。


    瓶颈在(f(i))的求法,我们考虑能不能不用通项公式,而是(O(n))递推出来。

    我们考虑在(n-1)对点的外面再加上一对点会有哪些方案:

    1. 新加点的边不染色,方案数是(f(n-1))
    2. 新加点之间的边染色,方案数是(f(n-1))
    3. 新加点的其中一个与另一边原来的(n-1)个点之间的边选一条染色,方案数是((n-1)f(n-1))
    4. 同理,新加点的另一个与另一边原来的(n-1)个点之间的边选一条染色,方案数是((n-1)f(n-1))
    5. 上面四种情况的方案数加起来,我们发现(4,5)可能与原来的点存在冲突,也就是我其中一个点和另一边原来点连边的时候,那个原来点可能与另一个原来点已经有染色边。这个怎么解决呢?我们可以假设如果有冲突,就把冲突的点连给另外那个新增点。举个例子:(a,b)是新增点,(a)往对面的原来点(x)连边,而(x)又已经与原来点(y)有一条染色边,那么我们把(x-y)这条边改成(b-y)。这样改动之后,不会有冲突,但是(4,5)中有算重的方案,所以我们还要减去:(a)与对面原来(n-1)个点连边,(b)与对面(n-1)个点连边的方案数(这其实是一个小容斥),为((n-1)^2*f[n-2])

    然后得到递推关系(f(n)=2n*f(n-1)-(n-1)^2*f[n-2])

    (因为我懒,没有画图XD,所以不太清楚的地方可以手动画图~


    转化一下问题

    棋盘问题可以转化为二分图来解决,那么这个二分图也可以转化为棋盘。

    把二分图的(X)部放在(x)轴,(Y)部放在(y)轴,那么每一个格点就代表了一条边,我们对格点染色。

    同种颜色的边不能在一个结点上,也就是同一行同一列只能有一个这个颜色,就是在棋盘上放车的问题。

    同样地,先考虑一种车,可以得到(f(n)=sum_{i=0}^nC_n^iA_n^i),即在(n)行中选出(i)行放车,选出来之后它们的列可以顺次有(n,n-1,n-2...n-i+1)种选择,当然也可以理解成有顺序地选。(这个似乎比二分图染色直观一点

    还是一样的容斥,钦定有(i)个两个颜色的车放在了同一个格点。(ans=sum_{i=0}^n (-1)^i*C(n,i)*A(n,i)*f^2(n-i))

    同样的考虑递推式。从(f(n-1))(f(n))相当于外面多了一圈格点共(2n-1)个出来。

    我们枚举选择周围的一圈格点,然后删掉那一行一列,剩下的(n-1)行,(n-1)列化归成(f(n))

    还有一种方案是外面这一圈一个都不选,所以一共有(2n)种,再乘上化归之后的(f (n-1))

    那个,我之前想错了,我以为是外面这一圈是或者的关系,然后乘上里面的那个原来的(n-1)的棋盘,那这样的方案(如下图)就没有办法算:

    但实际上是可以的,按照前面的正确思路理解,就是删掉选中的那一行那一列,剩下的最后一列还是在化归的方案里。

    但是还是有算重的情况,就是下图的情况,它在行,列里面都算过,要减去。

    (其实也可以想成之前的那种替换的想法,如果有冲突,就把冲突的点搬走,然后就相当于有了多出来的行列都有格子选的情况,但是这样会算重,就是一种方案可以是由行来算,冲突搬到列去,也可以是列来算,把冲突搬到行里去)


    ►Code View

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<set>
    using namespace std;
    #define N 10000005
    #define MOD 1000000007
    #define INF 0x3f3f3f3f3f3f3f3f
    #define LL long long
    LL rd()
    {
    	LL x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    	return f*x;
    }
    int n;
    LL fac[N],inv[N],f[N];
    LL ksm(LL a,LL b)
    {
    	LL res=1ll;
    	while(b)
    	{
    		if(b&1) res=res*a%MOD;
    		a=a*a%MOD;
    		b>>=1;
    	}
    	return res;
    }
    void Init()
    {
    	fac[0]=1,inv[0]=1;
    	for(int i=1;i<=N-5;i++)
    		fac[i]=fac[i-1]*i%MOD;
    	inv[N-5]=ksm(fac[N-5],MOD-2);
    	for(int i=N-6;i>=1;i--)
    		inv[i]=inv[i+1]*(i+1)%MOD;
    }
    LL C(int a,int b)
    {
    	return fac[a]*inv[a-b]%MOD*inv[b]%MOD;
    }
    LL A(int a,int b)
    {
    	return fac[a]*inv[a-b]%MOD;
    }
    int main()
    {
    	n=rd();
    	Init();
    	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;
    	//for(int i=2;i<=n;i++)
    	//	printf("%lld
    ",f[i]);
    	LL ans=0;
    	for(int i=0;i<=n;i++)
    	{
    		LL res=C(n,i)*A(n,i)%MOD*f[n-i]%MOD*f[n-i]%MOD;
    		if(i&1) ans=(ans-res+MOD)%MOD;
    		else ans=(ans+res)%MOD;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    /*
    f(n)=sum i=0->n C(n,i)*A(n,i)
    ans= sum i=0->n (-1)^i*C(n,i)*A(n,i)*f(n-i)^2
    f(n)=2n*f(n-1)-(n-1)^2*f(n-2)
    */
    
  • 相关阅读:
    ASP.NET Web API模型验证以及异常处理方式
    Javascript基础恶补
    求一个集合的集合下所有集合元素求值
    C#创建唯一的订单号, 考虑时间因素
    git的几十个基本面
    报错:ASP.NET Web API中找不到与请求匹配的HTTP资源
    使用RAML描述API文档信息的一些用法整理
    Postman测试Web API
    Javascript中的Prototype到底是啥
    AngularJS和DataModel
  • 原文地址:https://www.cnblogs.com/lyttt/p/14083419.html
Copyright © 2020-2023  润新知