题目链接
给点分配区间,如果两个点的区间相交,则两个点之间有边相连,否则没有。给你一棵树,求一棵最大的子树使得可以通过给点分配区间来得到这颗树。(n<=3e5)
画一画可以发现,一棵树合法,它的根结点可以有两个儿子向下生长,而每个非根结点只能有一个儿子向下生长:
这样就可以dp了,dp[u][0/1]表示u选一个儿子生长能得到的最多/次多结点,这样可以得到根节点的答案 = d p [ r o o t ] [ 0 ] + d p [ r o o t ] [ 1 ] − s z [ r o o t ] + 1 =dp[root][0]+dp[root][1]-sz[root]+1 =dp[root][0]+dp[root][1]−sz[root]+1
然后通过换根dp获得每个结点作为根时的答案。
换根dp要判断一下它父亲f的dp[f][0]是否由当前结点转移而来。
#include
#define ll long long
using namespace std;
const int maxn = 3e5 + 50;
vector<int> g[maxn];
int sz[maxn], dp[maxn][2], ans[maxn];
int n;
void init(){
scanf("%d", &n); for(int i = 1; i <= n; ++i) g[i].clear();
for(int i = 1; i < n; ++i) {
int u, v; scanf("%d%d", &u, &v);
g[u].push_back(v); g[v].push_back(u);
}
}
void dfs(int u, int f){
sz[u] = g[u].size();
if(u != 1) sz[u]--;
dp[u][0] = dp[u][1] = sz[u];
for(int i = 0; i < g[u].size(); ++i){
int v = g[u][i];
if(v == f) continue;
dfs(v, u);
int t = dp[v][0]+sz[u];
if(dp[u][0] < t){
dp[u][1] = dp[u][0];
dp[u][0] = t;
}
else if(dp[u][1] < t) dp[u][1] = t;
}
}
void DFS(int u, int f){
if(u != 1) dp[u][0]++, dp[u][1]++;
if( (dp[u][0]-1+sz[f] + (f != 1) ) != dp[f][0]){
//cout<<"u:"<
int t = dp[f][0]-1+sz[u]+(u!=1);
if(dp[u][0] < t){
dp[u][1] = dp[u][0];
dp[u][0] = t;
}
else if(dp[u][1] < t) dp[u][1] = t;
}
else{
int t = dp[f][1]-1+sz[u]+(u!=1);
if(dp[u][0] < t){
dp[u][1] = dp[u][0];
dp[u][0] = t;
}
else if(dp[u][1] < t) dp[u][1] = t;
}
ans[u] = dp[u][0] + dp[u][1] - g[u].size() + 1;
for(int i = 0; i < g[u].size(); ++i){
int v = g[u][i];
if(v == f) continue;
DFS(v, u);
}
}
void sol(){
dfs(1, 1);
//ans[1] = dp[1][0] + dp[1][1] - sz[1] + 1;
DFS(1, 0);
int mx = 0;
for(int i = 1; i <= n; ++i) {
//cout<<"i:"<
mx = max(mx, ans[i]);
}
printf("%d\n", mx);
}
int main()
{
int T; cin>>T;
while(T--){
init(); sol();
}
}