題解:P8973 『GROI-R1』 繼續深潛,為了同一個夢想

ccxswl發表於2024-10-04

換根 dp 模板題。

\(f_i\) 是在以 \(i\) 為根的子樹中,以 \(i\) 為鏈的一個端點且 \(i\) 在點集中的合法點集個數。
\(ans_i\) 表示包含 \(i\) 的合法點集個數。

\(x\) 為樹根時:

\[ans_x = {f_x \choose 2} - \sum_{s\in son}{2f_s+1 \choose 2} + f_x \]

簡單解釋一下,\({f_x \choose 2}\) 是在所有符合條件的鏈中隨便選兩條拼起來的數量,但是有可能會選到同一個子樹中的兩條鏈,這樣拼起來的集合就不合法了,就像樣例解釋中的 \(\{1,3,4\}\) 一樣,所以要減去。最後加上以 \(x\) 為鏈的一端的數量,也就是不需要拼的。

\(f_i\) 很好維護:

\[f_x= \sum_{s \in son}2f_s+1 \]

\(f_s\) 要乘 \(2\) 是因為 \(s\) 這個點可以選可以不選。即有可能是 \(\{\dots, s, x\}\),也有可能是 \(\{\dots, x\}\)
加一是因為要加上集合為 \(\{s, x\}\) 的情況。

換根的時候根據上面的公式改下 \(f\) 陣列就可以了。\(\sum{2f_s+1 \choose 2}\) 的部分用陣列預處理出來就好了。

具體實現看程式碼。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int read() {
	int s = 0, w = 1;
	char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')
			w = -w;
		c = getchar();
	}
	while (isdigit(c)) {
		s = s * 10 + c - 48;
		c = getchar();
	}
	return s * w;
}
void pr(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		pr(x / 10);
	putchar(x % 10 + 48);
}
#define end_ putchar('\n')
#define spc_ putchar(' ')

const int maxN = 5e5 + 7, mod = 1e9 + 7;

const int m2 = 500000004;
// 2 的逆元

int n;

vector<int> E[maxN];

int f[maxN], s[maxN], A[maxN], ans;
// s 陣列是預處理公式中求和的部分, A[i] 為 ans[i]

int C(int x) {
	return x * (x - 1 + mod) % mod * m2 % mod;
}

void dfs(int x, int fa) {
	for (int to : E[x])
		if (to != fa) {
			dfs(to, x);
			f[x] += f[to] * 2 + 1;
			f[x] %= mod;
			s[x] += C(f[to] * 2 + 1);
			s[x] %= mod;
		}
}

int ff[maxN];

void calc(int x, int fa) {
	A[x] = ((C(f[x]) - s[x] + mod) % mod + f[x]) % mod;

	for (int to : E[x]) {
		if (to == fa)
			continue;
		int fx = f[x], fto = f[to];
		int S = s[to];
		f[x] -= f[to] * 2 + 1;
		f[x] = (f[x] + mod * 2) % mod;
        // 這裡要注意乘 2,因為上面的 f[to] 乘 2 了,只加一個 mod 有可能不夠。

		f[to] += f[x] * 2 + 1;
		f[to] %= mod;

		s[to] += C(f[x] * 2 + 1);
		s[to] %= mod;

		calc(to, x);

		f[x] = fx, f[to] = fto;
		s[to] = S;
	}
}

signed main() {
	n = read();
	for (int i = 1; i < n; i++) {
		int u = read(), v = read();
		E[u].emplace_back(v);
		E[v].emplace_back(u);
	}
	dfs(1, 0);
	calc(1, 0);
	
	for (int i = 1; i <= n; i++)
		ans ^= A[i] * i;
	pr(ans), end_;
}

相關文章