大佬博客學習來自: https://blog.csdn.net/qq_40791842/article/details/99761346
大佬博客說這題涉及換根dp?我怎麼沒看出哪有換根dp思想?
枚舉去邊,那麼就變成了兩顆樹,這條邊提供的貢獻,就是這兩顆樹中的路徑。很妙,可是怎麼求去掉這條邊後的兩顆子樹的直徑呢?
這個就很麻煩。於是我採用看代碼學習。花了4個小時才從看懂加模仿打代碼AC,說明我樹形dp方面還是不足
貼題解圖:
代碼:
dp維護直徑和最長鏈
//dp1[1][i]:除去節點i爲子樹的以fa[i]爲終點的最長鏈
//dp1[0][i]:以節點i爲子樹的且以i爲終點的最長鏈
//dp2[1][i]:除去節點i爲子樹的樹直徑
//dp2[0][i]:以節點i爲子樹的樹直徑
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
#define pb push_back
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e5+10;
int n;
int mx[N*4];
vector<int>G[N];
int dp1[2][N],dp2[2][N];
void up(int id,int l,int r,int ql,int qr,int val)
{
if(ql<=l&&r<=qr)
{
mx[id]=max(mx[id],val);
return ;
}
int mid=l+r>>1;
if(ql<=mid) up(id<<1,l,mid,ql,qr,val);
if(qr>mid) up(id<<1|1,mid+1,r,ql,qr,val);
}
void pushdown(int id)
{
mx[id<<1]=max(mx[id<<1],mx[id]);
mx[id<<1|1]=max(mx[id<<1|1],mx[id]);
}
int qu(int id,int l,int r,int pos)
{
if(l==r) return mx[id];
int mid=l+r>>1;
if(mx[id]) pushdown(id);
if(pos<=mid) return qu(id<<1,l,mid,pos);
return qu(id<<1|1,mid+1,r,pos);
}
void getmax(int &fi,int &se,int val)//新添val更新最大、次大值
{
if(val>=fi)se=fi,fi=val;
else if(val>se) se=val;
}
void getmax(int &fi,int &se,int &th,int val)
{
if(val>=fi){th=se,se=fi,fi=val;}
else if(val>=se){th=se,se=val;}
else if(val>th){th=val;}
}
void dfs1(int u,int fa)
{
dp1[0][u]=dp2[0][u]=1;
int fi=0,se=0;
for(int v:G[u])
{
if(v==fa) continue;
dfs1(v,u);
dp1[0][u]=max(dp1[0][u],dp1[0][v]+1);//子樹最長鏈的更新
dp2[0][u]=max(dp2[0][u],dp2[0][v]);//子樹的最大直徑
getmax(fi,se,dp1[0][v]);
}
dp2[0][u]=max(dp2[0][u],fi+se+1);//子樹的最長鏈和次長鏈加本身
}
int newmax(int fi,int se,int val)
{
if(fi==val) return se;
return fi;
}
int newmax(int fi,int se,int th,int val)
{
if(val==fi) return se+th;
if(val==se) return fi+th;
return fi+se;
}
void dfs2(int u,int fa)
{
int sonlen=dp2[0][u],falen=dp2[1][u];
if(sonlen&&falen)
{
up(1,1,n,1,sonlen,falen);
up(1,1,n,1,falen,sonlen);
}
///更新dp1[1]
//最長鏈只需要更新子樹的最長即可
int fi=dp1[1][u],se=0,th=0;
for(int v:G[u])
{
if(v==fa) continue;
getmax(fi,se,th,dp1[0][v]);
}
for(int v:G[u])
{
if(v==fa) continue;
dp1[1][v]=newmax(fi,se,dp1[0][v])+1;
dp2[1][v]=newmax(fi,se,th,dp1[0][v])+1;
}
///dp2[1]先保存最長鏈組成的直徑
///更新dp2[1]
fi=dp2[1][u],se=0;
for(int v:G[u])
{
if(v==fa) continue;
getmax(fi,se,dp2[0][v]);
}
///然後更新由子樹的最大直徑更新
for(int v:G[u])
{
if(v==fa) continue;
int tmp=newmax(fi,se,dp2[0][v]);
dp2[1][v]=max(dp2[1][v],tmp);
}
for(int v:G[u]) if(v!=fa) dfs2(v,u);
}
int main()
{
int _;cin>>_;while(_--)
{
scanf("%d",&n);
memset(mx,0,sizeof(mx));
for(int i=1;i<n;++i)
{
int u,v;scanf("%d%d",&u,&v);
G[u].pb(v);
G[v].pb(u);
}
dfs1(1,-1);
dfs2(1,-1);
ll ans=0;
for(int i=1;i<=n;++i) ans+=qu(1,1,n,i);
printf("%lld\n",ans);
rep(i,1,n) G[i].clear();
}
}