zoukankan      html  css  js  c++  java
  • 【YBTOJ】【状压DP】图的计数

    图的计数

    ⼀个 DAG ,这个 DAG(m) 层,第⼀层只有⼀个源点,最后⼀层只有⼀个汇点,剩下的每⼀层都有 (k) 个节点。每次可以取反第 (i) 层和第 (i+1) 层之间的连边。也就是把原本从 ((i,k_1)) 连到 ((i+1,k_2)) 的边,变成从 ((i,k_2)) 连到 ((i+1,k_1))) 。请问有多少种取反的方案,把从源点到汇点的路径数变成偶数条?答案对 (998244353) 取模。

    (1le mle10000)(1le kle10).

    题解

    一道很恶心的题。

    • (f(i,j)) 表示当前在第 (i) 行,到其中某点奇偶性状态为 (j) 的方案数。
    • (a(i,j)) 表示第 (i)(j) 号点可到达的点对应状态,同理 (b(i,j)) 表示取反后(i)(j) 号点可到达的点对应状态。
    • 求奇偶性,可以用异或来转移。
    • 最后,统计最后一层的答案,并判断其奇偶性。

    代码

    #include <bits/stdc++.h>
    #define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    const int INF = 0x3f3f3f3f,N = 1e4+5,M = (1<<10)+5,mod = 998244353;
    typedef long long ll;
    typedef unsigned long long ull;
    inline ll read(){
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9'))ch=c,c=getchar();
    	while(c>='0'&&c<='9')ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n,m;
    int a[N][M],b[N][M]; 
    int dp[N][M];
    signed main(){
    	n = read() , m = read();
    	for(int i = 1 ; i <= m ; i ++)
    		if(read())
    			a[0][0] += 1<<(i-1);
    	dp[2][a[0][0]] = 1;
    	for(int i = 2 ; i <= n-2 ; i ++)
    		for(int k = 1 ; k <= m ; k ++)
    			for(int p = 1 ; p <= m ; p ++)
    				if(read())
    					a[i][k] += 1<<(p-1),
    					b[i][p] += 1<<(k-1);
    	
    	for(int i = 3 ; i <= n-1 ; i ++)
    		for(int j = 0 ; j < 1<<m ; j ++)
    			if(dp[i-1][j]){
    				int s1 = 0,s2 = 0;
    				for(int k = 1 ; k <= m ; k ++)
    					if(j & 1<<(k-1))
    						s1 ^= a[i-1][k] , s2 ^= b[i-1][k];
    				(dp[i][s1] += dp[i-1][j]) %= mod,
    				(dp[i][s2] += dp[i-1][j]) %= mod;
    //				printf(" dp[%d][%d] += [%d][%d](%d)
    ",i,s1,i-1,j,dp[i-1][j]);
    //				printf(" dp[%d][%d] += [%d][%d](%d)
    ",i,s2,i-1,j,dp[i-1][j]);
    			}
    //	puts("");
    	for(int i = 1 ; i <= m ; i ++)
    		if(read())
    			a[n-1][0] += 1<<(i-1);
    	int ans = 0;
    	for(int j = 0 ; j < 1<<m ; j ++){
    		int tot = 0;
    		for(int i = 1 ; i <= m ; i ++)
    			if((a[n-1][0] & (1<<(i-1))) && (j & (1<<(i-1)))) tot ^= 1;
    		if(!tot) 
    			(ans += dp[n-1][j]) %= mod;
    	}
    	printf("%d",ans);
    }
    
  • 相关阅读:
    se 键盘鼠标操作事件
    警告框操作方法(alert弹窗)
    se自带截图方法
    CSS Selector 高级用法
    吃奶酪
    互不侵犯
    hdu1102
    P4744 Iron man
    玉米田
    状压dp题单
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/15270861.html
Copyright © 2011-2022 走看看