2019 杭電第九場1007 Rikka with Travels

大佬博客學習來自: 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();
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章