• CF1425B Blue and Red of Our Faculty!


    CF1425B Blue and Red of Our Faculty!

    题目来源:Codeforces, 2020 ICPC, COMPFEST 12, Indonesia Multi-Provincial Contest (Unrated, Online Mirror, ICPC Rules, Teams Preferred), CF1425B Blue and Red of Our Faculty!

    题目大意

    题目链接

    给定一张 (n) 个点 (m) 条边的无向连通图,保证无自环,保证除节点 (1) 外每个点的度数都为 (2)

    有两人 Red 和 Blue 同时从节点 (1) 出发。初始时所有边都是灰色。Red 每经过一条边就会将它染成红色,Blue 每经过一条边就会将它染成蓝色。每轮中,每人会选择一条与当前所在节点相连的、灰色的边,并走到边的另一端。同一轮里两人选择的边不能相同

    当无法进行下一轮时,整个过程停止。问两人能走出多少种不同的最终局面,答案对 (10^9 + 7) 取模。两个最终局面不同当且仅当存在一条边,在最终局面下颜色不同。

    数据范围:(1leq nleq 2000)(1leq mleq 2n)

    本题题解

    根据“保证除节点 (1) 外每个点的度数都为 (2)”,可以发现整张图呈一个花瓣状。也就是有若干个环,节点 (1) 是它们的公共点。我们可以用一个序列(或者说可重集)({a_1,a_2,dots,a_k}) 来描述整张图,其中 (a_i) 表示第 (i) 个环的大小,(k) 是环的数量。

    考虑结束时两人的位置。可以分为三种情况:

    1. 两人都在节点 (1)。此时他们一定经过了所有环(图中没有灰边),且步数相同。方案数就相当于把 (a) 划分为两个集合,使他们和相等的方案数。
    2. 两人都在某个环的某个节点上(不含节点 (1))。设该环的大小为 (l),在环上 Red 走了 (x) ((1leq x < l)) 步,则 Blue 走了 (l - x) 步。两人的步数差为 ((l - x) - x = l - 2x)。问题相当于从 (a) 除当前环外的部分里(即 (asetminus{l})),选出两个互不相交的子集,使他们的和之差为 (l - 2x)
    3. 两人都在某个环上,且相距为 (1)(隔着一条边)。此时又可以分为两种情况:
      • 两人都不在节点 (1) 上(严格位于环上)。这与情况 2 类似,只不过此时两人的步数差为 (l - 1 - 2x),且 (1leq xleq l - 2)
      • 其中一人在节点 (1) 上。此时 (x = 0)(x = l - 1)。除了两人的步数差为 (l - 1 - 2x) 外,还需要保证其他环都被走过。也就是选出的两个子集的并为全集(全集,指:(a) 除去一个 (l) 外的其他所有元素,即 (asetminus{l}))。

    这三种情况,都可以用同一个 DP 来计算。

    先枚举 (l),从 (a) 中暂时删掉一个 (l)(如果是计算情况 (1),则不必删除任何元素)。

    ( ext{dp}(i,j, ext{flag})) 表示考虑了前 (i) 个环,当前 【Red 走的步数】(-)【Blue 走的步数】等于 (j)( ext{flag}) 是个 (0/1) 值,表示有没有两人都没走过的环。注意 (j) 这里,把“朴素地记录两人步数分别是多少”,变成“记录两者之差”,是个设计状态的小套路(CSP2019 d2t1 也有用到)。这么做是因为我们最终只关心它们的差。

    转移就考虑当前环,是被 Red 走,还是被 Blue 走,还是两人都不走:

    [egin{cases} ext{dp}(i - 1, j, ext{flag}) o ext{dp}(i, j + a_i, ext{flag})\ ext{dp}(i - 1, j, ext{flag}) o ext{dp}(i, j - a_i, ext{flag})\ ext{dp}(i - 1, j, ext{flag}) o ext{dp}(i, j, 0) end{cases} ]

    情况 1 的答案是 ( ext{dp}(k, 0, 1))(k) 是环的数量。

    情况 2 的答案是 (displaystyle 2 imes sum_{x = 1}^{l - 1}( ext{dp}(k - 1, l - 2x, 0) + ext{dp}(k - 1, l - 2x, 1)))。其中乘以 (2) 是因为最后一个环,Red 可以从两个方向(顺时针 / 逆时针)之一开始走。

    情况 3 的答案是 (displaystyle 2 imes left(sum_{x = 1}^{l - 2} ext{dp}(k - 1, l - 1 - 2x,0) + sum_{x = 0}^{l - 1} ext{dp}(k - 1, l - 1 - 2x,1) ight))

    一次 DP 的时间复杂度是 (mathcal{O}(n^2))。但是外层要枚举 (l)。虽然环的数量可能高达 (mathcal{O}(n)) 个,但由于 (sum ext{环长} = mathcal{O}(n)),所以不同的环长 (l) 只有 (mathcal{O}(sqrt{n})) 种。暴力枚举 (l) 再做 DP 的总时间复杂度 (mathcal{O}(n^2sqrt{n})),可以通过本题。另有一些优化方法:

    • 优化方法1:这是一个缺一背包的模型,所以可以用分治来优化,做到 (mathcal{O}(n^2log n))
    • 优化方法2:仍然暴力枚举 (l)。不对可重集 (a) 做 DP,而是把 (a) 缩成若干个二元组 (( ext{元素}, ext{出现次数})),然后类似于做多重背包。单次 DP 的时间复杂度是 (mathcal{O}(nsqrt{n})),总时间复杂度 (mathcal{O}(n^2))

    参考代码

    时间复杂度 (mathcal{O}(n^2sqrt{n}))

    // problem: CF1425B
    #include <bits/stdc++.h>
    using namespace std;
    
    #define mk make_pair
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    
    template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
    template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
    
    
    const int MAXN = 2000;
    const int MOD = 1e9 + 7;
    inline int mod1(int x) { return x < MOD ? x : x - MOD; }
    inline int mod2(int x) { return x < 0 ? x + MOD : x; }
    inline void add(int &x, int y) { x = mod1(x + y); }
    inline void sub(int &x, int y) { x = mod2(x - y); }
    
    
    int n, m;
    vector<int> G[MAXN + 5];
    
    int cir[MAXN + 5];
    int a[MAXN + 5], cnt;
    
    int dp[MAXN + 5][MAXN * 4 + 5][2];
    int calc_dp(int len) {
    	// 求两人最终在一个长度为 len 的环上相遇的方案数
    	// 特别地, len = 0 表示把所有环都完整地走一遍
    	
    	cnt = 0;
    	int sum = 0;
    	for (int i = 2; i <= n; ++i) {
    		for (int j = 1; j <= cir[i] - (i == len); ++j) {
    			a[++cnt] = i;
    			sum += i;
    		}
    	}
    	
    	for (int i = 0; i <= cnt; ++i) {
    		for (int j = -sum; j <= sum; ++j) {
    			for (int k = 0; k <= 1; ++k) {
    				dp[i][j + sum][k] = 0;
    			}
    		}
    	}
    	dp[0][0 + sum][1] = 1;
    	for (int i = 1; i <= cnt; ++i) {
    		for (int j = -sum; j <= sum; ++j) {
    			for (int k = 0; k <= 1; ++k) if (dp[i - 1][j + sum][k]) {
    				// red
    				assert(j + a[i] <= sum);
    				add(dp[i][j + a[i] + sum][k], dp[i - 1][j + sum][k]);
    				// blue
    				assert(j - a[i] >= -sum);
    				add(dp[i][j - a[i] + sum][k], dp[i - 1][j + sum][k]);
    				// neither
    				add(dp[i][j + sum][0], dp[i - 1][j + sum][k]);
    			}
    		}
    	}
    	
    	if (!len) {
    		return dp[cnt][0 + sum][1];
    	}
    	
    	int res = 0;
    	for (int i = 1; i < len; ++i) {
    		// red 走的
    		int dif = (len - i) - i;
    		if (abs(dif) > sum)
    			continue;
    		add(res, dp[cnt][dif + sum][0]);
    		add(res, dp[cnt][dif + sum][1]);
    	}
    	
    	for (int i = 0; i < len; ++i) {
    		int dif = (len - i - 1) - i;
    		if (abs(dif) > sum)
    			continue;
    		if (i != 0 && i != len - 1)
    			add(res, dp[cnt][dif + sum][0]);
    		add(res, dp[cnt][dif + sum][1]);
    	}
    	
    	res = res * 2 % MOD;
    	return res;
    }
    
    int main() {
    	cin >> n >> m;
    	for (int i = 1; i <= m; ++i) {
    		int u, v;
    		cin >> u >> v;
    		G[u].push_back(v);
    		G[v].push_back(u);
    	}
    	
    	static bool vis[MAXN + 5];
    	for (int i = 0; i < SZ(G[1]); ++i) {
    		int u = G[1][i];
    		if (vis[u]) {
    			continue;
    		}
    		
    		int len = 1;
    		int lst = 1;
    		while (u != 1) {
    			len++;
    			vis[u] = 1;
    			int nxt_u = (G[u][0] ^ G[u][1] ^ lst);
    			lst = u;
    			u = nxt_u;
    		}
    		cir[len]++;
    		// cerr << "found cir of len: " << len << endl;
    	}
    	
    	int ans = 0;
    	for (int i = 2; i <= n; ++i) {
    		if (cir[i]) {
    			add(ans, (ll)calc_dp(i) * cir[i] % MOD);
    		}
    	}
    	
    	add(ans, calc_dp(0));
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    HTML5项目笔记7:使用HTML5 WebStorage API构建与.NET对应的会话机制 Hello
    论设计模式和分析模式
    昨天我做了点什么事情啊?
    时间,时间,还是时间
    人生需要规划
    突然想起今天的博客汇报没写
    昨天看了熊猫大侠
    双休日往往会忘了写日志
    老婆说我是缺心眼!
    要下班了才想起没写报告
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/14209090.html
Copyright © 2020-2023  润新知