HDU 5293 Tree chain problem dfs序+树形dp

题意:
在树上给出一些链,链上有权值
求一个最大的链集合的权值和,集合内的链不相交
思路:
把每条链的状态放到lca(u,v)上,然后进行树形dp
dp[u]有两种状态
1.不取链
dp[u]=∑dp[Son[u]]
2.取一个lca(x,y)==u的链加上
因为要取这条链,所以我们应该减掉子树上取了相交的链多加的值
因为点有点多,而且对于链操作
很容易想到dfs序或者树剖(树剖太长了… 所以dfs序就好啦…
对于每个点,我们维护一下dp[u]-Sum[u]
Sum[u]=不取链的u的dp值=∑dp[Son[u]]
然后把整条链的dp[u]-Sum[u]全都减掉就好啦
dfs序有个特殊的技巧
就是把Out也多加一个点,但是在线段树上是*-1
这样我们在遍历父亲节点到右子树的上的点的链就直接In[u]~In[x]就好啦
因为左子树上的In会被Out减掉


好久没写blog了,因为要准备区域赛(懒得写….
希望能拿到不辜负自己的成绩吧…
但是不管结果怎样,总不会后悔参加这么好玩的比赛>_<

#pragma comment(linker, "/STACK:1024000000,1024000000") 
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<bitset>
#include<map>
#include<set>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long LL;
const int maxn = 200005;
const int inf=(1<<31)-1;
vector<int>vec[maxn];
vector<pair<int,pair<int,int> > >chain[maxn];
int In[maxn*2],Out[maxn*2],Zf[maxn*2],deep[maxn],tot;
int ft[maxn][20],Pow[20];
void dfs(int u,int fa,int dep)
{
    In[u]=++tot;
    Zf[tot]=1;
    deep[u]=dep;
    for(int i=1;i<20;++i)
    {
        if(dep<Pow[i])
            break;
        ft[u][i]=ft[ft[u][i-1]][i-1];
    }
    int Size=vec[u].size();
    for(int i=0;i<Size;++i)
    {
        int v=vec[u][i];
        if(v!=fa)
        {
            ft[v][0]=u;
            dfs(v,u,dep+1);
        }
    }
    Out[u]=++tot;
    Zf[tot]=-1;
}
int lca(int u,int v)
{
    if(deep[u]<deep[v])
        swap(u,v);
    int t=deep[u]-deep[v];
    for(int i=0;i<20;++i)
    if(t&Pow[i])
        u=ft[u][i];
    for(int i=19;i>=0;--i)
    if(ft[u][i]!=ft[v][i])
    {
        u=ft[u][i];
        v=ft[v][i];
    }
    if(u==v)
        return u;
    return ft[u][0];
}
int tree[maxn*4],lazy[maxn*4],Sub[maxn*4];
void Push_up(int rt)
{
    tree[rt]=tree[rt*2]+tree[rt*2+1];
}

void Push_down(int rt)
{
    if(lazy[rt])
    {
        lazy[rt*2]+=lazy[rt];
        lazy[rt*2+1]+=lazy[rt];
        tree[rt*2]+=lazy[rt]*Sub[rt*2];
        tree[rt*2+1]+=lazy[rt]*Sub[rt*2+1];
        lazy[rt]=0;
    }
}

void build(int l,int r,int rt)
{
    tree[rt]=lazy[rt]=0;
    if(l==r)
    {
        Sub[rt]=Zf[l];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    Sub[rt]=Sub[rt*2]+Sub[rt*2+1];
}

void Insert(int l,int r,int left,int right,int x,int rt)
{
    if(left<=l&&r<=right)
    {
        tree[rt]+=x*Sub[rt];
        lazy[rt]+=x;
        return ;
    }
    Push_down(rt);
    int mid=(l+r)/2;
    if(left<=mid)
        Insert(l,mid,left,right,x,rt*2);
    if(right>mid)
        Insert(mid+1,r,left,right,x,rt*2+1);
    Push_up(rt);
}

int Query(int l,int r,int left,int right,int rt)
{
    if(left<=l&&r<=right)
        return tree[rt];
    Push_down(rt);
    int tmp=0;
    int mid=(l+r)/2;
    if(left<=mid)
        tmp+=Query(l,mid,left,right,rt*2);
    if(right>mid)
        tmp+=Query(mid+1,r,left,right,rt*2+1);
    return tmp;
}

int dp[maxn],Sum[maxn];
void Dfs(int u,int fa)
{
    dp[u]=Sum[u]=0;
    int Size=vec[u].size();
    for(int i=0;i<Size;++i)
    {
        int v=vec[u][i];
        if(v!=fa)
        {
            Dfs(v,u);
            Sum[u]+=dp[v];
        }
    }
    dp[u]=Sum[u];
    Size=chain[u].size();
    for(int i=0;i<Size;++i)
    {
        int x=chain[u][i].first;
        int y=chain[u][i].second.first;
        int w=chain[u][i].second.second;
        int Ans=Sum[u]+w;
        Ans-=Query(1,tot,In[u],In[x],1);
        Ans-=Query(1,tot,In[u],In[y],1);
        //printf("*%d *%d\n",Query(1,tot,In[u],In[x],1),Query(1,tot,In[u],In[y],1));
        dp[u]=max(dp[u],Ans);
    }
    //printf("%d(dp: %d Sum: %d )\n",u,dp[u],Sum[u]);
    Insert(1,tot,In[u],In[u],dp[u]-Sum[u],1);
    Insert(1,tot,Out[u],Out[u],dp[u]-Sum[u],1);
}
int main()
{
    Pow[0]=1;
    for(int i=1;i<20;++i)
        Pow[i]=Pow[i-1]*2;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            vec[i].clear();
            chain[i].clear();
        }
        for(int i=1;i<n;++i)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            vec[u].push_back(v);
            vec[v].push_back(u);
        }
        tot=0;
        memset(ft,0,sizeof(ft));
        dfs(1,-1,0);
        //for(int i=1;i<=n;++i) printf("%d(%d,%d) ",i,In[i],Out[i]);printf("\n");
        //for(int i=1;i<=tot;++i) printf("%d ",Zf[i]);printf("\n");
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=m;++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            chain[lca(u,v)].push_back(make_pair(u,make_pair(v,w)));
        }
        build(1,tot,1);
        Dfs(1,-1);
        printf("%d\n",dp[1]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值