Rikka with Travels(树形dp)

original link - http://acm.hdu.edu.cn/showproblem.php?pid=6686

题意:

给出一棵树,两条路径不相交定义为不能有重复点,一个有向关系&lt;p1,p2&gt;&lt;p1,p2&gt;<p1,p2>,表示两天不相交的路径的点数为p1,p2p1,p2p1,p2,求有多少对。

解析:

要维护以下变量:

- 子树内与根相连的链的第一长,第二长和第三长,记为mx,mxx,mxxxmx,mxx,mxxxmx,mxx,mxxx
- 子树中的最长链,记为sizsizsiz


那么我们考虑向下dfsdfsdfs。传参有两个:
- 与子树不相交的,最长的链,记为upupup
- 与子树不相交的,以父亲为一端的链up2up2up2

那么做到一个点,维护的答案就是&lt;up,siz&gt;&lt;up,siz&gt;<up,siz>

之所以需要up2up2up2是因为向下搜索后,upupup可能从up2up2up2加上一条其他链得来。
mx,mxx,mxxxmx,mxx,mxxxmx,mxx,mxxx是因为upupup可能从其他两条链得来。
还要一种情况,upupup从其他分支的sizsizsiz得来。

up2up2up2的维护就是原来的up2+1up2+1up2+1,或是其他的链。


最后维护答案就是&lt;x,y&gt;&lt;x,y&gt;<x,y>,在线段树更新[1,x],[1,y][1,x],[1,y][1,x],[1,y]的最大值,最后的答案为所有位置的最大值之和。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2019-08-19-20.17.35
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define debug(i) printf("D_# %d\n",i)
const LL mod=1e9+7;
const int maxn=1e5+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/

int n;
int tr[maxn<<2];
int laz[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
void down(int rt){
    if(laz[rt]){
        laz[ls]=max(laz[ls],laz[rt]);
        laz[rs]=max(laz[rs],laz[rt]);
        tr[ls]=max(tr[ls],laz[rt]);
        tr[rs]=max(tr[rs],laz[rt]);
        laz[rt]=0;
    }
}
void update(int rt,int l,int r,int L,int R,int val){
    if(l>=L&&r<=R){
        tr[rt]=max(tr[rt],val);
        laz[rt]=max(laz[rt],val);
        return;
    }
    down(rt);
    if(L<=mid)update(ls,l,mid,L,R,val);
    if(R>mid)update(rs,mid+1,r,L,R,val);
    tr[rt]=max(tr[ls],tr[rs]);
}
int query(int rt,int l,int r,int pos){
    if(l==r){
        return tr[rt];
    }
    down(rt);
    if(pos<=mid)return query(ls,l,mid,pos);
    return query(rs,mid+1,r,pos);
}

#define rep_e(i,p,u) for(int i=head[p],u=to[i];i;i=nex[i],u=to[i])
int head[maxn],to[maxn<<1],nex[maxn<<1],now;
void add(int a,int b){
    nex[++now]=head[a];head[a]=now;to[now]=b;
}
void init_edge(){
    memset(head,0,sizeof head);
    now=0;
}

int mx[maxn],mxx[maxn],mxxx[maxn],siz[maxn];
void dfs(int p,int fa){
    mx[p]=mxx[p]=mxxx[p]=0;
    siz[p]=1;
    rep_e(i,p,u){
        if(u==fa)continue;
        dfs(u,p);
        siz[p]=max(siz[p],siz[u]);
        if(mx[u]+1>=mx[p]){
            mxxx[p]=mxx[p];
            mxx[p]=mx[p];
            mx[p]=mx[u]+1;
        }
        else if(mx[u]+1>=mxx[p]){
            mxxx[p]=mxx[p];
            mxx[p]=mx[u]+1;
        }
        else if(mx[u]+1>mxxx[p]){
            mxxx[p]=mx[u]+1;
        }
    }
    siz[p]=max(siz[p],mx[p]);
    if(mxx[p])siz[p]=max(siz[p],mx[p]+mxx[p]-1);
    if(mx[p]==0)mx[p]=1;
}

void Dfs(int p,int fa,int up,int up2){ // all up , up link fa
    if(p!=1)
        update(1,1,n,1,up,siz[p]),
        update(1,1,n,1,siz[p],up);
    int mxsiz=0,mxxsiz=0;
    rep_e(i,p,u){
        if(u==fa)continue;
        if(siz[u]>=mxsiz){
            mxxsiz=mxsiz;
            mxsiz=siz[u];
        }
        else if(siz[u]>mxxsiz){
            mxxsiz=siz[u];
        }
    }
    rep_e(i,p,u){
        if(u==fa)continue;
        int _up,_up2;
        int son1=0,son2=0;
        if(mx[u]+1==mx[p]){
            son1=mxx[p],
            son2=mxxx[p];
        }
        else if(mx[u]+1==mxx[p]){
            son1=mx[p],
            son2=mxxx[p];
        }
        else{
            son1=mx[p],
            son2=mxx[p];
        }
        if(!son1)
            _up=max(max(up,up2+1),1),
            _up2=max(1,up2+1);
        else if(!son2)
            _up=max(max(up,up2+son1),son1),
            _up2=max(son1,up2+1);
        else
            _up=max(max(up,up2+son1),son1+son2-1),
            _up2=max(son1,up2+1);

        if(mxsiz==siz[u]){
            if(mxxsiz){
                _up=max(_up,mxxsiz);
            }
        }
        else {
            _up=max(_up,mxsiz);
        }

        Dfs(u,p,_up,_up2);
    }
}

int main(){
    int t=rd(),cas=0;
    while(t--){
        cas++;
        mmm(tr,0);
        mmm(laz,0);
        init_edge();
        n=rd();
        rep(i,1,n-1){
            int a=rd(),b=rd();
            add(a,b);
            add(b,a);
        }

        dfs(1,-1);
        Dfs(1,-1,0,0);
        LL sum=0;
        rep(i,1,n-1){
            sum+=1ll*query(1,1,n,i);
        }
        printf("%lld\n",sum);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值