zoukankan      html  css  js  c++  java
  • Luogu P2595 [ZJOI2009]多米诺骨牌 容斥,枚举,插头dp,轮廓线dp

    真的是个好(毒)题(瘤)。其中枚举的思想尤其值得借鉴。

    (40pts):插头(dp),记录插头的同时记录每一列的连接状况,复杂度(O(N*M*2^{n + m} ))

    (100pts):容斥(+)插头(/)轮廓线。目前要维护每两行和每两列的限制,我们把两个限制分开讨论。预处理一下每个子矩阵如果不作限制的可行方案,然后人为地进行限制分割。对于列的限制用容斥解决,对于行的限制套在里面逐行枚举做一次(dp),就可以把复杂度降到可以接受的(O(m^3*2^n))

    代码有借鉴网上。并非完全原创。

    (40pts)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 15 + 5;
    const int base = 2999830;
    const int Mod = 19901013;
    const int M = 3000000 + 5;
    typedef unsigned int uint;
    
    int n, m, cur, las, cnt[2], can[N][N];
    
    int nxt[M], head[M], dp[2][M]; uint Hash[2][M];
    
    int get_wei (uint zt, int wei) {
    	return (zt >> wei) % 2;
    }
    
    int alt_wei (uint zt, int wei, int val) {
    	return zt + ((0ll + val - get_wei (zt, wei)) << wei);
    }
    
    void update (uint zt, int val) {
    	uint _zt = zt % base;
    	for (int i = head[_zt]; i; i = nxt[i]) {
    		if (Hash[cur][i] == zt) {
    			(dp[cur][i] += val) %= Mod; return;
    		}
    	}
    	nxt[++cnt[cur]] = head[_zt];
    	head[_zt] = cnt[cur];
    	Hash[cur][cnt[cur]] = zt;
    //	cout << "dp[" << cur << "][" << cnt[cur] << "] = " << val << endl;
    	dp[cur][cnt[cur]] = val;
    } 
    
    int get_val (uint zt) {
    	uint _zt = zt % base;
    	for (int i = head[_zt]; i; i = nxt[i]) {
    		if (Hash[cur][i] == zt) {
    			return dp[cur][i];
    		}
    	}
    	return 0;
    }
    
    void change_row () {
    	for (int i = 1; i <= cnt[cur]; ++i) {
    		uint &zt = Hash[cur][i];
    		if (get_wei (zt, m + m) == 0) {
    			dp[cur][i] = 0;//到不了下一行 
    		} 
    		for (int i = m; i >= 1; --i) {
    			zt = alt_wei (zt, i, get_wei (zt, i - 1));
    		}
    		zt = alt_wei (zt, 0, 0); // 全都向后移一位 
    		zt = alt_wei (zt, m + m, 0); // 到下一行的标记清空 
    	} 
    }
    
    int solve () {
    	update (1 << (m + m), 1);
    	for (int i = 1; i <= n; ++i) {
    		change_row ();
    		for (int j = 1; j <= m; ++j) {
    //			cout << "i = " << i << " j = " << j << endl;
    			las = cur, cur ^= 1, cnt[cur] = 0;
    			memset (head, 0, sizeof (head));
    			for (int k = 1; k <= cnt[las]; ++k) {
    				uint zt = Hash[las][k];
    				int b1 = get_wei (zt, j - 1);
    				int b2 = get_wei (zt, j - 0);
    				int val = dp[las][k];
    				if (val == 0) continue; // 不转移的小优化 
    //				cout << "b1 = " << b1 << " b2 = " << b2 << endl;
    				if (!can[i][j]) {
    					if (!b1 && !b2) {
    						update (zt, val);
    					}
    				} else {
    					if (b1 == 0 && b2 == 0) {
    						if (can[i][j + 1]) {
    							uint _zt = zt;
    							_zt = alt_wei (_zt, j - 0, 1); // 右插头改为 1 
    							_zt = alt_wei (_zt, m + j, 1); // j 列和 j + 1 列相连
    							update (_zt, val); 
    						}
    						if (can[i + 1][j]) {
    							uint _zt = zt;
    							_zt = alt_wei (_zt, j - 1, 1); // 下插头改为 1
    							_zt = alt_wei (_zt, m + m, 1); // 和下一行连通 
    							update (_zt, val);
    						}
    						update (zt, val); // 注意你并不需要放满所有没有障碍的格子
    					}
    					if (b1 + b2 == 1) { // 有一个连入的插头 
    						uint _zt = zt;
    						_zt = alt_wei (_zt, j - 0, 0);
    						_zt = alt_wei (_zt, j - 1, 0);
    						update (_zt, val);
    					}
    				}
    			}
    //			cout << "las : " << endl;
    //			for (int k = 1; k <= cnt[las]; ++k) {
    //				cout << "state = " << Hash[las][k] << " val = " << dp[las][k] << endl;
    //			}
    //			cout << "cur : " << endl;
    //			for (int k = 1; k <= cnt[cur]; ++k) {
    //				cout << "state = " << Hash[cur][k] << " val = " << dp[cur][k] << endl;
    //			}
    //			cout << endl;
    		}
    	}
    	uint fin = 0;
    	for (int i = m + 1; i < m + m; ++i) {
    		fin |= (1 << i); 
    	} 
    	return get_val (fin);
    }
    
    int main () {
    	cin >> n >> m;
    	for (int i = 1; i <= n; ++i) {
    		for (int j = 1; j <= m; ++j) {
    			int ch = getchar ();
    			while (ch != '.' && ch != 'x') {
    				ch = getchar ();
    			}
    			if (ch == '.') {
    				can[i][j] = true;
    			}
    		}
    	}
    //	for (int i = 1; i <= n; ++i) {
    //		for (int j = 1; j <= m; ++j) {
    //			cout << can[i][j] << " ";
    //		}
    //		cout << endl;
    //	}
    	cout << solve () << endl;
    }
    

    (100pts)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 15 + 5;
    const int M = 40000 + 5;
    const int Mod = 19901013;
    
    int n, m, top, ans; 
    int f[N], num[N], tmp[2][M], dp[N][N][N][N];
    
    bool mp[N][N];
    
    int ask (int u, int v) {
    	return (u >> v) % 2;
    }
    
    void work (int w, int u, int v) {
    	int las = 0, tot = (1 << (v - u + 1)) - 1;
    	for (int i = 0; i <= tot; ++i) {
    		tmp[0][i] = 0;
    	}
    	int t2 = 0;
    	for (int i = u; i <= v; ++i) {
    		if (mp[w][i]) {
    			t2 |= (1 << (i - u));
    		} 
    	}
    	tmp[0][t2] = 1;
    	for (int i = w + 1; i <= m + 1; ++i) {
    		int zt = 0;
    		for (int j = u, t = 0; j <= v; ++j, ++t) {
    			zt |= (mp[i][j] << t);
    			int cur = las ^ 1;
    			for (int k = 0; k <= tot; ++k) tmp[cur][k] = 0;
    			for (int k = 0; k <= tot; ++k) {
    				if(!tmp[las][k]) continue;
    				int t2 = k;
    				if (ask(t2,t) != mp[i][j]) {
    					t2 ^= (1 << t);
    				}
    				(tmp[cur][t2] += tmp[las][k]) %= Mod;
    				if (ask (k, t)) continue;
    				if (j != v && !ask (k, t + 1)) {
    					t2 = k | (1 << (t + 1));
    					if (ask (t2, t) != mp[i][j]) {
    						t2 ^= (1 << t);
    					}
    					(tmp[cur][t2] += tmp[las][k]) %= Mod;
    				}
    				if(!mp[i][j]) {
    					t2 = k | (1 << t);
    					(tmp[cur][t2] += tmp[las][k]) %= Mod;
    				}
    			}
    			las = cur;
    		}
    		dp[w][u][i - 1][v] = tmp[las][zt];
    	}
    }
    
    int main () {
    	cin >> m >> n;
    	for(int i = 1; i <= m; ++i) {
    		for (int j = 1; j <= n; ++j) {
    			int ch = getchar ();
    			while (ch != '.' && ch != 'x') {
    				ch = getchar ();
    			}
    			mp[i][j] = (ch == 'x');
    		}
    	}
    	for (int i = 1; i <= n; ++i) {
    		for (int j = i; j <= n; ++j) {
    			for (int k = 1; k <= m; ++k) {
    				work (k, i, j);
    			}
    		}
    	}
    	
    	int tot = (1 << (n - 1)) - 1;
    	for(int i = 0; i <= tot; ++i) {
    		int top = 1;
    		num[top] = 0;
    		for (int j = 0; j < n - 1; ++j) {
    			if (ask (i, j)) num[++top] = j + 1;
    		}
    		num[++top] = n;
    		for (int d = 1; d <= m; ++d) {
    			for (int u = 1; u <= d; ++u) {
    				int t = 1;
    				for(int j = 2; j <= top; ++j) {
    					int p = num[j - 1] + 1, q = num[j];
    					t = (1ll * t * dp[u][p][d][q]) % Mod;
    				}
    				if (u == 1) {
    					f[d] = t;
    				} else {
    					f[d] = (0ll + f[d] - (1ll * t * f[u - 1])) % Mod;
    				}
    			}
    		}
    		(f[m] += Mod) %= Mod; 
    		if (top % 2 == 1) {
    			(ans -= f[m]) %= Mod;
    		} else { 
    			(ans += f[m]) %= Mod;
    		}
    	}
    	cout << (ans + Mod) % Mod << endl;
    }
    
  • 相关阅读:
    [你必须知道的.NET] 第四回:后来居上:class和struct
    [你必须知道的.NET]第十回:品味类型值类型与引用类型(下)-应用征途
    [你必须知道的.NET]第十一回:参数之惑传递的艺术(上)
    [你必须知道的.NET] 第一回:恩怨情仇:is和as
    [Anytao.History] 排名进入1000,未来值得努力
    [你必须知道的.NET] 第三回:历史纠葛:特性和属性
    [你必须知道的.NET] 第八回:品味类型值类型与引用类型(上)-内存有理
    [你必须知道的.NET] 第五回:深入浅出关键字把new说透
    [你必须知道的.NET]第十二回:参数之惑传递的艺术(下)
    .NET 3.5
  • 原文地址:https://www.cnblogs.com/maomao9173/p/10832633.html
Copyright © 2011-2022 走看看