题目链接:传送门
题意:给你一棵树(注意是棵树),让你求断开一个点后不同联通块组成的最大结点对数,然后求添加一条边后剩下的最少的不同联通块组成的结点对数。
思路:暴力的去搜断开每个点的节点对数的复杂度是O(n^2),ttt。所以我们要利用回溯实现O(n)的dfs。然后再记录一下最大的两个联通块数量就行了。
附上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 1000000000;
const int MAXN = 1000100;
int n;
int head[10050];
int vis[10050];
int tt = -1;
int maxx = -1;
int cnt;
struct Edge {
int to, next;
}edge[10050*2];
void add_edge(int bg, int ed) {
cnt++;
edge[cnt].to = ed;
edge[cnt].next = head[bg];
head[bg] = cnt;
}
int dfs(int x) {
int tot = 0;
int sum = 0;
vis[x] = 1;
for (int i = head[x]; i!=-1; i = edge[i].next) {
int v = edge[i].to;
if (!vis[v]) {
int t1 = dfs(v);
sum += t1*(n - t1);
tot += t1;
}
}
sum += tot*(n - tot);
sum /= 2;
if (sum > maxx) {
maxx = sum;
tt = x;
}
return tot + 1;
}
int dfs2(int x){
int tot = 1;
vis[x] = 1;
for (int i = head[x];i!=-1; i = edge[i].next){
int v = edge[i].to;
if (!vis[v]){
tot += dfs(v);
}
}
return tot;
}
int main(void) {
int u, v;
cnt = 0;
scanf("%d", &n);
memset(head, -1, sizeof(head));
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d%d", &x, &y);
add_edge(x, y);
add_edge(y, x);
}
dfs(0);
memset(vis, 0, sizeof(vis));
vis[tt] = 1;
int max1 = 0;
int max2 = 0;
for (int i = head[tt]; i!=-1; i = edge[i].next) {
int v = edge[i].to;
if (!vis[v]) {
int t1 = dfs2(v);
if (t1 > max1) {
max2 = max1;
max1 = t1;
}
else if (t1 > max2) {
max2 = t1;
}
}
}
printf("%d %d\n", maxx, maxx - max1*max2);
}