Paths升级
来源:互联网 发布:企业注销 淘宝企业店铺 编辑:程序博客网 时间:2024/06/04 23:20
考试题的Path的升级版,不是求最多的路径数量,而是求选哪些路径的价值和最大,这样就没有办法贪心了。
知识点:
1、很明显的能看出来这是一道树形dp题。
2、每个点之间的关系要和路径连上关系,树上的路径是固定的,那就有一个lca。
3、为了计算dp值,要求路径上的点的值,又要更新,那就要用到线段树。
4、树上的线段树,又要用到dfs序。
然后分条来讲
1、树形dp这就没法讲了,看不出来也没有办法
2、树上的路径都有两个端点,又要求路径上的每个点的相关数据,那就要知道一个lca,方便计算
3、dp的定义是以i为根时权值最大为dp[i],还要一个sum[i],用来计算i的每个子节点的dp值的和,转移时dp[i]=max(sum[i],sum[i]+ALLsum[k]-ALLdp[k]),其中k是以i为lca的一条路径的节点。
这样讲肯定看不懂,可以这么理解。
现在有一条路径,如果要选这条路径,那么这条路径上的所有点不能作为其他路径上的点,那么就要收集路径上的sum和dp,用sum-dp就行了。dp是要减掉路径上的点的可能性。
4、dfs序在这里简单,只要记录一下L、R就行。线段树里装的是这个点到1点的路径上的sum值得和与dp值得和。那这棵子树上就是Query(x)+Query(y)-Query(lca)*2 就行了。
还要注意只有当所有子节点都算出来后才能算父节点。
#include<bits/stdc++.h>#define M 100005using namespace std;struct node1{int x,y,lca,z;};vector<node1>G[M];vector<int>edge[M];int fa[18][M],dep[M],n,m,dp[M],sum[M];int R[M],L[M],T;struct node2{int L,R,s,d;}tree[4*M];void Build(int L,int R,int p){//以下的Build,Query,Change都是线段树,写的有点麻烦,大致是单点查询,区间更新 tree[p].L=L;tree[p].R=R; tree[p].s=tree[p].d=0; if(L==R){ return; } int mid=(L+R)/2; Build(L,mid,p*2);Build(mid+1,R,p*2+1);}int Querys(int L,int R,int p){ if(tree[p].L==L&&tree[p].R==R)return tree[p].s; int mid=(tree[p].L+tree[p].R)/2; if(mid>=R)return Querys(L,R,2*p)+tree[p].s; else if(mid<L)return Querys(L,R,2*p+1)+tree[p].s; else return Querys(L,mid,2*p)+Querys(mid+1,R,2*p+1)+tree[p].s;}int Queryd(int L,int R,int p){ if(tree[p].L==L&&tree[p].R==R)return tree[p].d; int mid=(tree[p].L+tree[p].R)/2; if(mid>=R)return Queryd(L,R,2*p)+tree[p].d; else if(mid<L)return Queryd(L,R,2*p+1)+tree[p].d; else return Queryd(L,mid,2*p)+Queryd(mid+1,R,2*p+1)+tree[p].d;}void Change(int L,int R,int p,int sumk,int dpk){ if(tree[p].L==L&&tree[p].R==R){ tree[p].s+=sumk;tree[p].d+=dpk; return; } int mid=(tree[p].L+tree[p].R)/2; if(mid>=R)Change(L,R,2*p,sumk,dpk); else if(mid<L)Change(L,R,2*p+1,sumk,dpk); else Change(L,mid,2*p,sumk,dpk),Change(mid+1,R,2*p+1,sumk,dpk);}void f(int x,int fa1){//造树,dfs序 fa[0][x]=fa1;dep[x]=dep[fa1]+1; L[x]=++T; for(int i=0;i<(int)edge[x].size();i++){ int y=edge[x][i]; if(y==fa1)continue; f(y,x); } R[x]=T;}void Init(){//LCA的预处理 for(int j=1;j<18;j++) for(int i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]];}int LCA(int x,int y){//求两点LCA if(dep[x]>dep[y])swap(x,y); int step=dep[y]-dep[x]; for(int i=0;i<18;i++) if(step&(1<<i))y=fa[i][y]; if(x==y)return x; for(int i=17;i>=0;i--) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y]; return fa[0][x];} void f1(int x,int fa1){ for(int i=0;i<(int)edge[x].size();i++){//先算完所有字点 int y=edge[x][i]; if(y==fa1)continue; f1(y,x); sum[x]+=dp[y]; } dp[x]=sum[x]; for(int i=0;i<(int)G[x].size();i++){//用所有lca为x的路径来更新 int X=G[x][i].x,Y=G[x][i].y; int sumk=Querys(L[X],L[X],1)+Querys(L[Y],L[Y],1); int dpk=Queryd(L[X],L[X],1)+Queryd(L[Y],L[Y],1); dp[x]=max(dp[x],sum[x]+G[x][i].z+sumk-dpk); } Change(L[x],R[x],1,sum[x],dp[x]);//算完更新}void solve(){ scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); edge[x].push_back(y); edge[y].push_back(x); } f(1,0);Init(); for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); int lca=LCA(x,y); G[lca].push_back((node1){x,y,lca,z});//按照lca放 } Build(1,n,1); f1(1,0); printf("%d\n",dp[1]);}void Clear(){//多组数据,要清空 T=0; memset(fa,0,sizeof fa); memset(dep,0,sizeof dep); memset(dp,0,sizeof dp); memset(sum,0,sizeof sum); memset(L,0,sizeof L); memset(R,0,sizeof R); for(int i=1;i<=n;i++)edge[i].clear(); for(int i=1;i<=n;i++)G[i].clear();}int main(){ int cas; scanf("%d",&cas); while(cas--){ solve(); Clear(); } return 0;}
阅读全文
0 0
- Paths升级
- Paths(升级版)---3011
- Paths 升级(DFS序+线段树+LCA+树形DP)
- Paths
- Paths
- Paths
- Paths
- Paths
- Paths
- Paths
- Paths
- Paths
- Paths
- Paths
- 升级
- 升级
- 升级
- 升级
- 基于QT的汽车销售管理
- Android BroadcastReveiver广播
- 实验二单链表
- CodeForces 868A Bark to Unlock
- Verilog写83编码器
- Paths升级
- java打印正方形
- 【NOJ】[1007] 第几天
- Mysql性能优化实战:数据库锁的介绍与索引查找原理
- 坚持#第209天~了解Linux操作系统和负载均衡
- 使用Adaboost训练车牌定位——(3)
- hdu3072 Intelligence System
- Java学习前准备(1)
- 几款常用的在线API管理工具(是时候抛弃office编写接口文档了)