CF 1425B Blue and Red of Our Faculty!

  • 给出 \(m\) 个环,它们两两之间有且仅有一个交点 \(1\),求出有多少种选择出 两条起点为 \(1\) 长度相同的路径分别记为 \(Red,Blue\),且边没有重复,并且不能够再拓展 的方案数,两种方案不同当且仅当至少对应的 \(Red,Blue\) 中至少有一个选取边的集合不同,保证环长度的总和小于 \(4000\)。

显然最后结束时两人相距不超过 \(1\),否则它们均可以再走一步。

每人走的路径集合必定有若干个完整的环,可以再接一段残缺的环。

那么考虑按照最终的位置分别统计。

在 \(1\) 处相遇,那么就是将所有环划分为两个不同集合,求每个集合的环长度之和相等。

均走进了同一个环,且相距为 \(0/1\),那么就是选出一个环分配给两人正整数长度,再将剩下的由两人分,可以不选某个环,总和相同的方案数。

只有 \(1\) 人走进环并且距离,另外一人在 \(1\) 处,相距为 \(1\),那么就是选出一个环的长度减 \(1\),剩下的由两人分完,总和相同的方案数。

都可以用类似于背包的方法搞。

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair <int, int> P;
const int inf = 0x3f3f3f3f, N = 2005, mod = 1e9 + 7;
template <typename T>
void rd_(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x*10 + ch - '0';x *= f;
}

vector <int> h[N];
int n, m, s[N], tot, f[2][N<<2][2], cur, g[2][N<<2], ans;
bool vis[N];

void dfs_(int u) {
	vis[u] = 1, s[tot]++;
	for (int v, i = 0; i < (int) h[u].size(); i++) {
		v = h[u][i];
		if (vis[v]) continue;
		dfs_(v);
	}
}

int main() {
	rd_(n), rd_(m);
	for (int u, v, i = 1; i <= m; i++) {
		rd_(u), rd_(v);
		h[u].push_back(v), h[v].push_back(u);
	}
	vis[1] = 1;
	rep (i, 0, (int) h[1].size() - 1) {
		int u = h[1][i];
		if (!vis[u]) {
			s[++tot] = 1;
			dfs_(u);
		}
	}
	f[0][m][0] = 1;
	rep (i, 1, tot) {
		cur ^= 1;
		rep (j, 0, 2*m) {
			f[cur][j][0] = f[cur^1][j][0];
			f[cur][j][1] = f[cur^1][j][1];
			if (j >= s[i]) {
				f[cur][j][0] = (f[cur][j][0] + f[cur^1][j - s[i]][0])%mod;
				f[cur][j][1] = (f[cur][j][1] + f[cur^1][j - s[i]][1])%mod;
			}
			if (j + s[i] <= 2*m) {
				f[cur][j][0] = (f[cur][j][0] + f[cur^1][j + s[i]][0])%mod;
				f[cur][j][1] = (f[cur][j][1] + f[cur^1][j + s[i]][1])%mod;
			}
			rep (k, 1, s[i] - 1) {
				int d = 2*k - s[i];
				if (j >= d) f[cur][j][1] = (f[cur][j][1] + f[cur^1][j - d][0]*2ll)%mod;
			}
			rep (k, 1, s[i] - 2) {
				int d = 2*k - s[i] + 1;
				if (j >= d) f[cur][j][1] = (f[cur][j][1] + f[cur^1][j - d][0]*2ll)%mod;
			}
		}
	}
	ans = f[cur][m][1];
	g[cur = 0][m] = 1;
	rep (i, 1, tot) {
		cur ^= 1;
		rep (j, 0, 2*m) {
			g[cur][j] = 0;
			if (j >= s[i]) g[cur][j] = (g[cur][j] + g[cur^1][j - s[i]])%mod;
			if (j + s[i] <= 2*m) g[cur][j] = (g[cur][j] + g[cur^1][j + s[i]])%mod;
		}
	}
	ans = (ans + g[cur][m])%mod;
	memset(f, 0, sizeof(f));
	f[cur = 0][m][0] = 1;
	rep (i, 1, tot) {
		cur ^= 1;
		rep (j, 0, 2*m) {
			f[cur][j][0] = 0;
			f[cur][j][1] = 0;
			if (j >= s[i]) {
				f[cur][j][0] = (f[cur][j][0] + f[cur^1][j - s[i]][0])%mod;
				f[cur][j][1] = (f[cur][j][1] + f[cur^1][j - s[i]][1])%mod;
			}
			if (j + s[i] <= 2*m) {
				f[cur][j][0] = (f[cur][j][0] + f[cur^1][j + s[i]][0])%mod;
				f[cur][j][1] = (f[cur][j][1] + f[cur^1][j + s[i]][1])%mod;
			}
			if (j + s[i] - 1 <= 2*m)
				f[cur][j][1] = (f[cur][j][1] + f[cur^1][j + s[i] - 1][0]*2ll)%mod;
			if (j - (s[i] - 1) >= 0)
				f[cur][j][1] = (f[cur][j][1] + f[cur^1][j - s[i] + 1][0]*2ll)%mod;
		}
	}
	ans = (ans + f[cur][m][1])%mod;
	printf("%d\n", ans);
}
上一篇:IT帮助台KPI系列之:服务中断损失


下一篇:5万字用纯C语言从零开始实现人脸检测