HDU 5293(树链剖分)

(http://acm.hust.edu.cn/vjudge/contest/view.action?cid=106364#problem/A)
题意:给定一棵树,一些链,选出最多的链不相交。
解法:定义 sum[u] = sim(dp[v]) ( v = son[u] )
dp[u]为u这颗子树,最多能选的链数。
当有一条链a,b,lca(a,b)=u
dp[u] = sum[u] - dp[a1] + sum[a1] - dp[a2] … + sum[a]
这样就可以用树链剖分搞啦~

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <queue>
//#include <tr1/unordered_set>
//#include <tr1/unordered_map>
#include <bitset>
//#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define inf 1e9
#define debug(a) cout << #a" = " << (a) << endl;
#define debugarry(a, n) for (int i = 0; i < (n); i++) { cout << #a"[" << i << "] = " << (a)[i] << endl; }
#define clr(x, y) memset(x, y, sizeof x)
#define ll long long
#define ull unsigned long long
#define FOR(i,a,b) \
    for(i=a;a<b?i<=b:i>=b;a<b?i++:i--)

const int maxn = 100000+30;

struct sad{
    int to,next;
}G[maxn<<2];
int h[maxn],si;
void add(int u,int v){
    G[si].to=v;
    G[si].next=h[u];
    h[u]=si++;
}
int siz[maxn],dep[maxn];
int fa[maxn],son[maxn],top[maxn];
void dfs1(int u,int f,int d)
{
    fa[u]=f;
    dep[u]=d;
    siz[u]=1;
    son[u]=-1;
    for(int i=h[u];~i;i=G[i].next)
    {
        int v=G[i].to;
        if(v!=f){
            dfs1(v,u,d+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[son[u]]<siz[v])
                son[u]=v;
        }
    }
}
int p[maxn],fp[maxn],pos;
void dfs2(int u,int sf){
    top[u]=sf;
    p[u]=pos++;
    fp[p[u]]=u;
    if(son[u]==-1) return ;
    dfs2(son[u],sf);
    for(int i=h[u];~i;i=G[i].next)
    {
        int v=G[i].to;
        if(son[u]!=v&&fa[u]!=v)
            dfs2(v,v);
    }
}
int rs[maxn];
void upp(int x,int p)
{
    while(x<=pos)
    {
        rs[x] += p;
        x += x&-x;
    }
}
int get(int x){
    int ret=0;
    while(x)
    {
        ret+=rs[x];
        x-=x&-x;
    }
    return ret;
}

int Query(int u,int v)
{
    int ret=0,f1=top[u],f2=top[v];
    while(f1^f2)
    {
        if(dep[f1]<dep[f2])
        {
            swap(f1,f2);
            swap(u,v);
        }
        ret += get(p[u]) - get(p[f1]-1);
        u=fa[f1];
        f1=top[u];
    }
    if( dep[u] > dep[v] ) swap(u,v);
    return ret += get(p[v]) - get(p[u]-1);
}
int lca(int u,int v){
    int f1=top[u],f2=top[v];
    while(f1^f2)
    {
        if(dep[f1]<dep[f2])
        {
            swap(f1,f2);
            swap(u,v);
        }
        u=fa[f1];
        f1=top[u];
    }
    if( dep[u] > dep[v] ) swap(u,v);
    return u;
}
void init()
{
    clr(h,-1);
    si=0;
    pos=1;
}

int uu[maxn],vv[maxn],val[maxn];
int dp[maxn] , sum[maxn];
vector<int>Q[maxn];

void dfs(int u,int f)
{
    for(int i=h[u];~i;i=G[i].next)
    {
        int v = G[i].to;
        if( v != f )
        {
            dfs(v,u);
            sum[u] += dp[v];
        }
    }
    upp(p[u],sum[u]);
    dp[u] = sum[u];
    for(int i=0;i<Q[u].size();i++)
    {
        int j=Q[u][i];
        dp[u] = max( dp[u] , Query(uu[j],vv[j]) + val[j] );
    }
    upp(p[u],-dp[u]);
}

int main()
{
//  freopen("input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    int n,m;
    while(T--){
        scanf("%d%d",&n,&m);
        init();
        int u,v;
        for(int i=0;i<n-1;i++){
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        clr(dp,0);
        clr(sum,0);
        clr(rs,0);
        for(int i=1;i<=n;i++)
            Q[i].clear();
        dfs1(1,-1,1);
        dfs2(1,1);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&uu[i],&vv[i],&val[i]);
            Q[lca(uu[i],vv[i])].push_back(i);
        }
        dfs(1,-1);
        printf("%d\n",dp[1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值