1. 程式人生 > 其它 >[題解][CF-1292C]Xenon‘s Attack on the Gangs

[題解][CF-1292C]Xenon‘s Attack on the Gangs

\(sz_{root}(u)\)表示以\(root\)為根,子樹\(T(u)\)的大小, \(fa_{root}(u)\)表示以\(root\)為根,節點\(u\)的父親。這兩個東西可以\(O(n^2)\)預處理。

轉化一下問題:

\[ans=\sum\limits_{x = 1}^{n - 1} \sum\limits_{u,v \in V} [\mathrm{mex}(u,v) \ge x] \]

那麼現在考慮從小到大加入每一個邊權,首先加入邊權\(0\)

那麼一定可以得到\(sz_{u}(v) \times sz_{v}(u)\)的貢獻。那麼接下來加入權值\(1\)

假如說\(0,1\)

不是連著的,那麼兩條邊夾著的所有點對答案的就只能是\(0\),(如圖)

接著想想,將\(0,1\)連在一起有利無害,那麼就連在一起吧。。。這啟示我們,接下來放邊權的時候,一定是儘量將之前已經放了邊權形成的路徑延長一個位置(這個想法比較古怪)。也就是說,對於\(\mathrm{mex}\)最長的一條路徑,它所包含的邊權可以組成\([0,路徑長度-1]\)這一個區間的,而且區間的兩個端點一定在葉子上。接下來設定一條最長的路徑\((u,v)\),我們想想怎麼在上面填邊權。首先在某個位置填上\(0\),然後任選左右其中位置填上\(1\),然後再選左右其中一個填上\(2\)。每次填上一個邊權之後,在答案中加入路徑兩邊連著的子樹的大小。這不就是\(dp\)

嗎?設\(f(u,v)\)表示形成了路徑\((u,v)\)得到的最大\(ans\)

那麼可以得到:

\[f(u,v)=\begin{cases} \max\left\{f(fa_{v}(u),v), f(u, fa_{u}(v)\right\} + sz_{u}(v)\times sz_{v}(u) & u \ne v\\ 0 & u = v \end{cases} \]
#include <bits/stdc++.h>
#define LL long long

using namespace std;

const int maxn = 3e3 + 5;
struct Edge {
	int v, nex;
	Edge(int v = 0, int nex = 0) : v(v), nex(nex) {}
} E[maxn << 1];
int hd[maxn], tote;

void addedge(int u, int v) {
	E[++tote] = Edge(v, hd[u]), hd[u] = tote;
	E[++tote] = Edge(u, hd[v]), hd[v] = tote; 
} 

LL sz[maxn][maxn], fat[maxn][maxn], f[maxn][maxn];
int n, rt;

void init(int u, int fa) {
	fat[rt][u] = fa, sz[rt][u] = 1;
	for (int i = hd[u]; i; i = E[i].nex) {
		int v = E[i].v;
		if (v == fa) continue;
		init(v, u), sz[rt][u] += sz[rt][v];
	}
}

LL dp(int u, int v) {
	if (u == v) return 0;
	if (~f[u][v]) return f[u][v];
	return f[u][v] = max(dp(fat[v][u], v), dp(u, fat[u][v])) + sz[u][v] * sz[v][u];
}

int main() {
	memset(f, -1, sizeof(f));
	scanf("%d", &n);
	for (int i = 1; i < n; i++) {
		int u, v; scanf("%d%d", &u, &v);
		addedge(u, v);
	}
	for (int i = 1; i <= n; i++) rt = i, init(i, 0);
	LL ans = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) ans = max(ans, dp(i, j));
	printf("%lld\n", ans);
	return 0;
}