Comet OJ - Contest #2 D 枚舉重心
阿新 • • 發佈:2019-04-28
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 枚舉重心