hdu5293 lca+dp+树状数组+时间戳

题意是给了 n 个点的树,会有m条链条 链接两个点,计算出他们没有公共点的最大价值,  公共点时这样计算的只要在他们 lca 这条链上有公共点的就说明他们相交

dp[i]为这个点包含的子树所能得到的最大价值

sum[i]表示这个点没有选择经过i这个点链条的总价值

两种选择

这个点没有被选择

dp[i]=sum[i]=sigma(dp[k])k为i的子树

选择了某个链

假设这条链 为(tyuijk)

那么dp[i]=(sum[i]-dp[u]-dp[j])+(sum[j]-dp[k])+dp[k] +(sum[u]-dp[y])+(sum[y]-dp[t])+sum[t];

整理后发现 dp[i]=sum[i] +(sum[j]-dp[j])+(sum[k]-dp[k])+(sum[u]-dp[u])+(sum[y]-dp[y])+(sum[t]-dp[t]);

使用lca计算出每条链的最近公共祖先,在这个最近公共祖先上判断是否使用这条链,还有我们可以使用时间戳加树状数组来求得sum和dp

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=100000+10;
int to[maxn*2],nx[maxn*2],H[maxn*2],numofedg,timoflook;
int fa[maxn][20],first[maxn],last[maxn],depth[maxn];
void addedg(int u, int v)
{
     numofedg++; to[numofedg]=v; nx[numofedg]=H[u]; H[u]=numofedg;
     numofedg++; to[numofedg]=u; nx[numofedg]=H[v]; H[v]=numofedg;
}
void dfs(int cur, int per, int dep)
{
    first[cur]=++timoflook;
    depth[cur]=dep;
    fa[cur][0]=per;
    for(int i=1; i<20; i++)
    {
        fa[cur][i]=fa[ fa[cur][i-1] ][ i-1 ];
    }
    for(int i=H[cur]; i; i=nx[i])
        {
            if(to[i]==per)continue;
            dfs(to[i],cur,dep+1);
        }
    last[cur]=++timoflook;
}
int getlca(int u,int v)
{
     if(depth[u]<depth[v])swap(u,v);
     for(int i=19; i>=0; i--)
        {
             if(depth[fa[u][i]]>=depth[v])
                u=fa[u][i];
             if(u==v)return u;
        }
     for(int i=19; i>=0; i--)
        {
             if(fa[u][i]!=fa[v][i])
             {
                 u=fa[u][i];
                 v=fa[v][i];
             }
        }
        return fa[u][0];
}
struct Edg
{
  int u,v,lca,val;
}P[maxn];
vector<int>E[maxn];
int dp[maxn],sum[maxn],CS[maxn*3],CD[maxn*3];
int lowbit(int x)
{
     return x&-x;
}
void add(int x, int d, int *C)
{
      while(x<=timoflook)
        {
             C[x]+=d;
             x+=lowbit(x);
        }
}
int getsum(int x, int *C)
{
     int ret=0;
      while(x>0)
        {
            ret+=C[x];
            x-=lowbit(x);
        }
        return ret;
}
void solve(int cur, int per)
{
     dp[cur]=sum[cur]=0;
     for(int i=H[cur]; i; i=nx[i])
        {
            if(to[i]==per)continue;
            solve(to[i],cur);
            sum[cur]+=dp[to[i]];
        }
     dp[cur]=sum[cur];
     for(int i=0; i<E[cur].size(); i++)
        {
              int id=E[cur][i];
              int u=P[id].u;
              int v=P[id].v;
              int t1=getsum(first[u],CS);
              int t2=getsum(first[v],CS);
              int t3=getsum(first[u],CD);
              int t4=getsum(first[v],CD);
              int tmp=t1+t2-t3-t4;
              dp[cur]=max(dp[cur],tmp+P[id].val+sum[cur]);
        }
     add(first[cur],sum[cur],CS);
     add(last[cur],-sum[cur],CS);
     add(first[cur],dp[cur],CD);
     add(last[cur],-dp[cur],CD);

}
int main()
{
    int cas;
    scanf("%d",&cas);
    for(int cc=1; cc<=cas; cc++)
        {
              int n,m;
              timoflook=numofedg=0;
              scanf("%d%d",&n,&m);
              for(int i=0; i<=n; i++)
                {
                    CS[i*2]=CS[i*2+1]=CD[i*2]=CD[i*2+1]=0;
                    H[i]=0;E[i].clear();

                }

              for(int i=1; i<n; i++)
                {
                    int u,v;
                    scanf("%d%d",&u,&v);
                    addedg(u,v);
                }
                fa[1][0]=1;
                dfs(1,1,0);
                for(int i=0; i<m; i++)
                    {
                           scanf("%d%d%d",&P[i].u,&P[i].v,&P[i].val);
                           P[i].lca=getlca(P[i].u,P[i].v);
                           E[P[i].lca].push_back(i);
                    }
                solve(1,-1);
                printf("%d\n",dp[1]);
        }
    return 0;
}

时间: 2024-09-29 22:08:20

hdu5293 lca+dp+树状数组+时间戳的相关文章

UESTC 912 树上的距离 --LCA+RMQ+树状数组

1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离) 2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的节点到根节点的距离,很明显,DFS一遍后以v为根的子树在DFS序列中是连续的一段,及转化为区间更新问题,可以用树状数组. 做法:先把求LCA解决,LCA可以转化为RMQ问题,可参见:LCA转RMQ, 即转化为LCA(T,u,v) = RMQ(B,pos[u],pos[v]),其中B为深度序列.预先DFS可以处

HDU 2227 Find the nondecreasing subsequences (DP+树状数组+离散化)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2227 Find the nondecreasing subsequences                                  Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                             

树形DP+树状数组 HDU 5877 Weak Pair

1 //树形DP+树状数组 HDU 5877 Weak Pair 2 // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 3 // 这道题要离散化 4 5 #include <bits/stdc++.h> 6 using namespace std; 7 #define LL long long 8 typedef pair<int,int> pii; 9 const double inf = 12345678901234

poj 3616 Milking Time dp+树状数组

题意: 给一堆区间,每个区间有开始时间s,结束时间e,和收益w,现在要找一些区间使收益和最大,且区间之间的间隔最小为r. 分析: 这道题用dp做是简单题,用dp+树状数组做是中等题.dp问题的关键是对状态的定义.有两种方法,一:dp[k]表示按开始时间排序到第k个区间能取得的最大收益.二:dp[t]表示在时间t时能获得的最大收益.定义好状态方程就好写了这不再赘述.有趣的是这个时间复杂度.设一共有M个区间,所有区间的最大时间为L,第一种是M^2的,第二种是M*(logL+logM)的,这题M才10

奶牛抗议 DP 树状数组

奶牛抗议 DP 树状数组 USACO的题太猛了 容易想到\(DP\),设\(f[i]\)表示为在第\(i\)位时方案数,转移方程: \[ f[i]=\sum f[j]\;(j< i,sum[i]-sum[j]\ge0) \] \(O(n^2)\)过不了,考虑优化 移项得: \[ f[i]=\sum f[j]\;(j< i,sum[i]\ge sum[j]) \] 这时候我们发现相当于求在\(i\)前面并且前缀和小于\(sum[i]\)的所有和,这就可以用一个树状数组优化了,在树状数组维护下标为

HDU5293(SummerTrainingDay13-B Tree DP + 树状数组 + dfs序)

Tree chain problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1798    Accepted Submission(s): 585 Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,-,n.The

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

第十四个目标(dp + 树状数组)

Problem 2236 第十四个目标 Accept: 17    Submit: 35 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description 目暮警官.妃英里.阿笠博士等人接连遭到不明身份之人的暗算,柯南追踪伤害阿笠博士的凶手,根据几起案件现场留下的线索发现凶手按照扑克牌的顺序行凶.在经过一系列的推理后,柯南发现受害者的名字均包含扑克牌的数值,且扑克牌的大小是严格递增的,此外遇害者与毛利小五郎有关. 为了避免

Gym - 100269F Flight Boarding Optimization(dp+树状数组)

原题链接 题意: 现在有n个人,s个位置和你可以划分长k个区域你可以把s个位置划分成k个区域,这样每个人坐下你的代价是该区域内,在你之前比你小的人的数量问你怎么划分这s个位置(当然,每个区域必须是连续的),才能使得总代价最小,输出代价. 分析:dp[i][j]表示第i个位置是第j个区域的结尾,dp[i][j]→dp[t][j+1]暴力转移.但是需要预处理每个范围里的代价值,需要树状数组维护. #include<bits/stdc++.h> using namespace std; const