【BZOJ5315】【JSOI2018】防御网络

【题目链接】

【思路要点】

  • 将边分为两种,一种是树上的边,一种是环上的边。
  • 树上的边有贡献,当且仅当其两侧均有点被选取,简单统计即可。
  • 环上的边有贡献,当且仅当其所在的环上相邻两个子树内被选取了一些点的点之间的区间不是环上最长的,同一个环上的边的贡献可以通过DP解决,时间复杂度\(O(N^3)\)。
  • 总时间复杂度\(O(N^3)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 405;
const int MAXM = 805;
const int P = 1e9 + 7;
typedef pair <int, int> num;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
struct edge {int dest, home; };
vector <edge> a[MAXN];
int n, m, tot, ans, x[MAXM], y[MAXM];
int depth[MAXN], father[MAXN], fhome[MAXN];
bool calced[MAXM], vis[MAXN];
void dfs(int pos) {
	tot++, vis[pos] = true;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (!vis[a[pos][i].dest]) dfs(a[pos][i].dest);
}
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
void calc(int cnt, int *a) {
	static int val[MAXN];
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= cnt; i++)
		vis[a[i]] = true;
	for (int i = 1; i <= cnt; i++) {
		tot = 0; dfs(a[i]);
		val[i] = power(2, tot) - 1;
		val[i + cnt] = val[i];
	}
	for (int i = 1; i <= cnt; i++)
	for (int j = i + 1; j < i + cnt; j++) {
		num now = (num) {cnt - (j - i), i};
		static int dp[MAXN]; int sum = 0, pos = i;
		dp[i] = sum = val[i];
		for (int k = i + 1; k <= j; k++) {
			while (pos < k && (num) {k - pos, k > cnt ? k - cnt : k} > now) {
				sum = (sum - dp[pos] + P) % P;
				pos++;
			}
			dp[k] = 1ll * sum * val[k] % P;
			sum = (sum + dp[k]) % P;
		}
		ans = (ans + 1ll * (j - i) * dp[j]) % P;
	}
}
void work(int pos, int fa, int from) {
	depth[pos] = depth[fa] + 1;
	father[pos] = fa;
	fhome[pos] = from;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (depth[a[pos][i].dest] == 0) work(a[pos][i].dest, pos, a[pos][i].home);
		else if (depth[a[pos][i].dest] <= depth[pos] - 2) {
			static int p[MAXN];
			int cnt = 0, tmp = pos;
			p[++cnt] = tmp; calced[a[pos][i].home] = true;
			while (tmp != a[pos][i].dest) {
				calced[fhome[tmp]] = true;
				tmp = father[tmp];
				p[++cnt] = tmp;
			}
			calc(cnt, p);
		}
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++) {
		read(x[i]), read(y[i]);
		a[x[i]].push_back((edge) {y[i], i});
		a[y[i]].push_back((edge) {x[i], i});
	}
	work(1, 0, 0);
	for (int i = 1; i <= m; i++) {
		if (calced[i]) continue;
		memset(vis, false, sizeof(vis));
		vis[x[i]] = vis[y[i]] = true;
		int tmp = 1;
		tot = 0; dfs(x[i]); tmp = 1ll * tmp * (power(2, tot) - 1) % P;
		tot = 0; dfs(y[i]); tmp = 1ll * tmp * (power(2, tot) - 1) % P;
		ans = (ans + tmp) % P;
	}
	printf("%lld\n", 1ll * ans * power(power(2, n), P - 2) % P);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80434012