Codeforces 979E Kuro and Topological Parity(dp)

题面传送门

题意:有 \(n\) 个点,每个点要么被涂黑,要么被涂白,要么没有颜色。
现在你要:

  1. 给没有颜色的点图上颜色(黑色或白色)
  2. 在这 \(n\) 个点中连若干条有向边,可以不连通。但是只能从编号小的点连向编号大的点,且不能有重边和自环。

定义一条路径 \(p_1 \to p_2 \to \dots p_k\) 是好的,当且仅当对于 \(i \in [1,k-1]\)\(c_{p_i} \neq c_p_{i+1}\)
特别地,一个点组成的路径也是好的。
求有多少种涂色+构图的方法使得原图中好的路径的个数模 \(2\)\(k\),答案对 \(10^9+7\) 取模。
\(1 \leq n \leq 50\),其实可以加强到 \(1 \leq n \leq 10^5\)

先抛开构图不谈,考虑对于已知的图 \(G\),怎样求它的好的路径的条数。
由于我们只能从编号小的点向编号大的点连边,那么原序列的一种拓扑序一定是 \(1,2,3,\dots n\)
用拓扑排序的方法,设 \(g_x=\)\(x\) 结尾的好的路径的条数。
那么 \(g_x=\sum\limits_{(y,x)\in E \& c_y\neq c_x}g_y+1\),非常好理解,枚举上一个点转移,或者单独成一条路径。
总条数 \(=\sum g_i\)

回到本题上来,本题的 \(g_x\) 都是在 \(\mod 2\) 意义上的。
分析上面那个 \(g_x\) 的式子,不难发现,假设我们要连指向 \(x\) 的边,那么对于 \(y<x\)\(c_y=c_x\) 的点,边 \((y,x)\) 存不存在是无关紧要的,因为它不会影响 \(g_x\) 的奇偶性。
同理,\(g_y \mod 2=0\)\(y\) 也不会对 \(g_x\) 的奇偶性产生影响。
那么现在问题就在于 \(g_y \mod 2=1\)\(c_x \neq c_y\) 的点存不存在。
假设存在至少 \(1\)这样的点,考虑随便连剩下 \(i-2\) 条边,共 \(2^{i-2}\) 条边,那么会有怎样的情况呢?

  • 如果这 \(i-2\) 条边对 \(g_x\) 的贡献为奇数,那么连边 \((y,x)\) 会导致 \(g(x)\mod 2=0\),反之 \(g(x)\mod 2=1\)
  • 如果这 \(i-2\) 条边对 \(g_x\) 的贡献为偶数,那么连边 \((y,x)\) 会导致 \(g(x)\mod 2=1\),反之 \(g(x)\mod 2=0\)

由此可见,无论剩下 \(i-2\) 条边连或不连,你都可以控制 \((y,x)\) 的连或不连来达到你想要的奇偶性
这个性质对我们的解法有极大的启发性。
\(f_{i,j,b,w}\) 表示:

  • 考虑到第 \(i\) 个点
  • 目前好的路径的总条数 \(\mod 2\)\(j\)
  • 是否存在 \(g_x \mod 2=1\) 的黑点的状态为 \(b\)
  • 是否存在 \(g_x \mod 2=1\) 的白点的状态为 \(w\)

采用正推进行转移。枚举 \(i+1\) 号点填的颜色,这里以黑色为例。

  • 如果 \(w=1\),由以上性质,\(g_{i+1}\) 为奇数和偶数的情况各占一半,各 \(2^{i-1}\),再根据乘法原理乘个 \(dp_{i,j,b,w}\) 即可。
  • 如果 \(w=0\),由以上性质,\(g_1,g_2,\dots,g_i\) 要么对 \(g_{i+1}\) 没有贡献,要么对 \(g_{i+1}\) 的贡献为偶数。再加上原本就有的 \(1\),故对于所有 \(2^i\) 种连法,都有 \(g_{i+1}\) 为奇数。乘法原理转移即可。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define mp make_pair
typedef long long ll;
typedef pair<int,int> pii;
const int MOD=1e9+7;
inline void inc(int &x,int y){x+=y;if(x>=MOD) x-=MOD;}
int n,p,a[55],dp[55][2][2][2],pw2[55];
//dp[i][j][b][w]
//we have connected edges among the first i points
//the parity of the number of good paths at present is j
//whether there exists "odd black" is b
//whether there exists "odd white" is w
int main(){
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	pw2[0]=1;
	for(int i=1;i<=n;i++) pw2[i]=pw2[i-1]*2%MOD;
	if(a[1]!=1) dp[1][1][1][0]=1;
	if(a[1]!=0) dp[1][1][0][1]=1;
	for(int i=1;i<n;i++) for(int j=0;j<2;j++)
		for(int b=0;b<2;b++) for(int w=0;w<2;w++){
			if(a[i+1]!=1){//black
				if(w){
					inc(dp[i+1][j^1][1][1],1ll*dp[i][j][b][w]*pw2[i-1]%MOD);//g[i+1] is odd
					inc(dp[i+1][j][b][1],1ll*dp[i][j][b][w]*pw2[i-1]%MOD);//g[i+1] is even
				}
				else inc(dp[i+1][j^1][1][0],1ll*dp[i][j][b][w]*pw2[i]%MOD);
			}
			if(a[i+1]!=0){//white
				if(b){
					inc(dp[i+1][j^1][1][1],1ll*dp[i][j][b][w]*pw2[i-1]%MOD);
					inc(dp[i+1][j][1][w],1ll*dp[i][j][b][w]*pw2[i-1]%MOD);
				}
				else inc(dp[i+1][j^1][0][1],1ll*dp[i][j][b][w]*pw2[i]%MOD);
			}
		}
//	for(int i=1;i<=n;i++) for(int j=0;j<2;j++)
//		for(int b=0;b<2;b++) for(int w=0;w<2;w++)
//			printf("%d %d %d %d %d\n",i,j,b,w,dp[i][j][b][w]);
	int ans=0;
	for(int b=0;b<2;b++) for(int w=0;w<2;w++)
		inc(ans,dp[n][p][b][w]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-11-04 18:49  tzc_wk  阅读(151)  评论(2)    收藏  举报