1. 程式人生 > >Comet OJ - Contest #2 D 枚舉重心

Comet OJ - Contest #2 D 枚舉重心

lld 暴力枚舉 name ace http 代碼 n) www. fine

題面

思路:

函數f相當於是求一個點集f的直徑,有一個性質是如果這個點集有多個直徑一定相交於某一個點,或者一條邊的中心,所以我們暴力枚舉重心,計算以某個點為重心的點集對答案的貢獻。

具體實現的時候,我們從一個重心開始深搜,計算其它點到這個點的距離。我們現在假設計算以當前點為重心,有多少個點集的直徑是i。首先,之前所有半徑小於i / 2的點隨便選了,假設有sum個,那前面的點有2 ^ sum種情況。假設半徑是i / 2的點有cnt[i]個,那只有這些點才可能構造出i的直徑,並且,這兩個點不能在一個連通塊中(把重心去掉後可能會形成若幹個連通塊), 所以我們枚舉每個連通塊中半徑是i / 2的點數,此時假設其它連通塊中沒有點半徑是i / 2,減去這些情況就可以了。

代碼:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 4010;
const LL mod = 998244353;
int head[maxn], Next[maxn * 2], ver[maxn * 2], tot;
LL cnt[maxn], re[maxn][maxn], p[maxn], ans[maxn];
int n;
void add(int x, int y) {
	ver[++tot] = y;
	Next[tot] = head[x];
	head[x] = tot;
}
void dfs(int x, int fa, int deep, int dye) {
	if(x <= n) {
		cnt[deep]++;
		re[dye][deep]++;
	}
	for (int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == fa) continue;
		dfs(y, x, deep + 1, dye);
	}
}
int main() {
	int x, y;
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		scanf("%d%d", &x, &y);
		add(x, i + n), add(i + n, x), add(y, i + n), add(i + n, y);
	}
	p[0] = 1;
	for (int i = 1; i <= 2 * n - 1; i++) p[i] = (p[i - 1] * 2) % mod;
	for (int i = 1; i <= 2 * n - 1; i++) {
		int dye = 0;
		LL sum = 0;
		memset(cnt, 0, sizeof(cnt));
		if(i <= n) {
			cnt[0]++;
			sum = 1; 
		}
		for (int j = head[i]; j; j = Next[j]) {
			int y = ver[j];
			dye++;
			memset(re[dye], 0, sizeof(re[dye]));
			dfs(y, i, 1, dye);
		}
		for (int j = 1; j < n; j++) {
			LL now = p[cnt[j]] - 1;
			for (int k = 1; k <= dye; k++)
				now = (now - (p[re[k][j]] - 1) + mod) % mod;
			ans[j] = (ans[j] + (now * p[sum]) % mod) % mod;
			sum += cnt[j];
		}
	}
	for (int i = 1; i < n; i++)
		printf("%lld\n", ans[i]);
} 

  

Comet OJ - Contest #2 D 枚舉重心