HDU 5293 Tree chain problem [树链剖分+线段树+树形DP]

Description

Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n. 
There are m chain on the tree, Each chain has a certain weight. Coco would like to pick out some chains any two of which do not share common vertices. 
Find out the maximum sum of the weight Coco can pick 


题意:给出N个点的树,M条树链,树链有权,问在取出的树链互不相交的情况下,权值和最大是多少。

解法:可以这么考虑,若某点作为一个点对X,Y的LCA,那么如果取了这条链,其子树的解最大是多少呢?

设DP[x]表示以x为根的子树中,取完以此点为lca的链后能取得的最大值。那么可以知道,取了某条链后(此链的lca为x)其能获得的最大值为 Σ DP[{son}] +W 其中{son}是此链的儿子集合(不包括链本身),那么我们可以dfs处理,dfs到某一个点x,处理以x点为lca的所有链,计算出取了每条链后能获得的值的最大值,将其赋值给dp[X]。

那么怎么求Σ DP[{son}] ? 我们可以这么处理:每次求出一个dp[X],将 -dp[X] 加到X点的VAL[],+dp[X]加到X点的父亲的VAL[],那么 Σ VAL[链上的点] 就是所求的答案。很明显,求链上点权和 可以用树链剖分+线段树写,复杂度为N*logN*logN。(实际上,因为此题没有修改,可以DFS序+树状数组快速处理 O(N*logN))

代码:(ZKW线段树 1200+MS)

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

#define NN 100100

struct node{
    int x,y,w;
}a[NN];
vector<node> q[NN];
int son[NN],sz[NN],top[NN],pos[NN],dep[NN],f[NN][20],pn;
int ptx[NN],lnum,dp[NN];
int n,qn;
struct edge{
    int v,next;
    edge(){}
    edge(int v,int next){
        this->v=v;
        this->next=next;
    }
}ed[NN*2];
void addline(int x,int y){
    ed[lnum]=edge(y,ptx[x]);
    ptx[x]=lnum++;
}
void init(int n){
    pn=lnum=0;
    rep(i,1,n)ptx[i]=-1;
    rep(i,1,n)q[i].clear();
}
//tree chain divide
int getson(int x,int fa){
    int val=0;
    son[x]=0;
    sz[x]=1;
    f[x][0]=fa;
    dep[x]=dep[fa]+1;
    gson(i,x){
        int y=ed[i].v;
        if(y==fa)continue;
        sz[x]+=getson(y,x);
        if(sz[y]>val){
            val=sz[y];
            son[x]=y;
        }
    }
    return sz[x];
}
void getchain(int r,int x,int fa){
    top[x]=r;
    pos[x]=++pn;
    if(son[x])getchain(r,son[x],x);
    gson(i,x){
        int y=ed[i].v;
        if(y==fa||y==son[x])continue;
        getchain(y,y,x);
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    drep(i,16,0)if(dep[f[x][i]]>=dep[y])x=f[x][i];
    if(x==y)return x;
    drep(i,16,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
//segment tree
int m,val[NN*4];
void build(int n){
    for(m=1;m<n+3;m<<=1);
    rep(i,1,m<<1)val[i]=0;
}
int query(int l,int r){
    int sum=0;
    for(l=l+m-1,r=r+m+1;l^r^1;l>>=1,r>>=1){
        if(~l&1)sum+=val[l^1];
        if(r&1) sum+=val[r^1];
    }
    return sum;
}
inline void update(int pos,int v){
    for(pos=pos+m;pos;pos>>=1)val[pos]+=v;
}
//solve
void calc(int z){
    int sz=q[z].size()-1;
    rep(i,0,sz){
        int x=q[z][i].x;
        int y=q[z][i].y;
        int w=q[z][i].w;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            w+=query(pos[top[x]],pos[x]);
            x=f[top[x]][0];
        }
        if(dep[x]>dep[y])swap(x,y);
        w+=query(pos[x],pos[y]);
        dp[z]=max(w,dp[x]);
    }
    update(pos[z],-dp[z]);
    if(f[z][0])update(pos[f[z][0]],dp[z]);
}
void solve(int x,int fa){
    dp[x]=0;
    gson(i,x){
        int y=ed[i].v;
        if(y==fa)continue;
        solve(y,x);
        dp[x]+=dp[y];
    }
    calc(x);
}
int main(){
    tdata{
        scanff(n);scanff(qn);
        init(n);
        build(n);
        rep(i,1,n-1){
            int x;scanff(x);
            int y;scanff(y);
            addline(x,y);
            addline(y,x);
        }
        getson(1,0);
        getchain(1,1,0);
        rep(k,1,16)
        rep(i,1,n)f[i][k]=f[f[i][k-1]][k-1];
        rep(i,1,qn){
            scanff(a[i].x);
            scanff(a[i].y);
            scanff(a[i].w);
            q[lca(a[i].x,a[i].y)].pb(a[i]);
        }
        solve(1,0);
        printf("%d\n",dp[1]);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值