CF 1000G Two-Paths (树形DP)
Posted guapisolo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF 1000G Two-Paths (树形DP)相关的知识,希望对你有一定的参考价值。
题目大意:给你一棵树,点有点权$a_{i}$,边有边权$w_{e}$,定义一种路径称为$2-path$,每条边最多经过2次且该路径的权值为$sum _{x} a_{x};-;sum_{e}w_{e}cdot k_{e}$,$k_{e}$为边的经过次数,一共$Q$次询问,每次查询经过$x,y$的$2-path$权值最大的路径的权值
看题解之前感觉不可做...有点思路但感觉不靠谱,都被我否掉了
LiGuanlin神犇提供了一种树形DP的解法
定义$f[x][0]$表示该子树内,经过$x$点的$2path$路径的最大权值$-a[x]$的值
$f[x][1]$表示x点父树的子树内,不经过$x$点的路径的最大权值
$f[x][2]$表示该节点从父节点过来的$2path$路径的最大权值
$dfs$2次搜出$f$,再维护前缀和,转移即可
注意需要倍增$lca$来找到$lca(x,y)$最靠近$x$的儿子,来去掉它个贡献
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N 301000 5 #define uint unsigned int 6 #define ll long long 7 #define mod 1000000007 8 using namespace std; 9 10 int gint() 11 { 12 int ret=0,f=1;char c=getchar(); 13 while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();} 14 while(c>=‘0‘&&c<=‘9‘){ret=ret*10+c-‘0‘;c=getchar();} 15 return ret*f; 16 } 17 int n,q,cte; 18 int a[N],fe[N],fa[N],head[N]; 19 ll f[N][3],g[N][2],vsum[N],esum[N]; 20 struct Edge{int to,nxt,val;}edge[N*2]; 21 void ae(int u,int v,int w){ 22 cte++;edge[cte].to=v,edge[cte].val=w; 23 edge[cte].nxt=head[u],head[u]=cte; 24 } 25 int use[N],dep[N]; 26 ll dis[N],sum[N]; 27 int ff[N][20]; 28 void dfs1(int u,int dad) 29 { 30 ff[u][0]=u; 31 for(int j=head[u];j;j=edge[j].nxt){ 32 int v=edge[j].to; 33 if(v==dad) continue; 34 dep[v]=dep[u]+1,fa[v]=u,ff[v][1]=u,fe[v]=edge[j].val; 35 dis[v]=dis[u]+edge[j].val; 36 vsum[v]=vsum[u]+a[v]; 37 esum[v]=esum[u]+edge[j].val; 38 dfs1(v,u); 39 if(f[v][0]+a[v]-2ll*edge[j].val>0) 40 use[v]=1,f[u][0]+=f[v][0]+a[v]-2ll*edge[j].val; 41 } 42 } 43 void dfs2(int u) 44 { 45 for(int j=head[u];j;j=edge[j].nxt){ 46 int v=edge[j].to; 47 if(v==fa[u]) continue; 48 if(use[v]){ 49 f[v][1]=f[u][0]-(f[v][0]+a[v]-2ll*edge[j].val); 50 f[v][2]=max(0ll,f[u][2]+f[v][1]+a[u]-2ll*edge[j].val); 51 }else{ 52 f[v][1]=f[u][0]; 53 f[v][2]=max(0ll,f[u][2]+f[v][1]+a[u]-2ll*edge[j].val); 54 } 55 g[v][0]=g[u][0]+f[v][0]; 56 g[v][1]=g[u][1]+f[v][1]; 57 dfs2(v); 58 } 59 } 60 void get_lca() 61 { 62 for(int j=2;j<=19;j++) 63 for(int i=1;i<=n;i++) 64 ff[i][j]=ff[ ff[i][j-1] ][j-1]; 65 } 66 int Lca(int x,int y,int &fx,int &fy) 67 { 68 int px=x,py=y,ans,dx,dy,flag=1; 69 if(dep[x]<dep[y]) swap(x,y); 70 for(int i=19;i>=0;i--) 71 if(dep[ff[x][i]]>=dep[y]) x=ff[x][i]; 72 for(int i=19;i>=0;i--) 73 if(ff[x][i]!=ff[y][i]) x=ff[x][i],y=ff[y][i]; 74 else ans=ff[x][i]; 75 x=px,y=py; 76 dx=dep[ans]+1; 77 dy=dep[ans]+1; 78 for(int i=19;i>=0;i--){ 79 if(dep[ff[x][i]]>=dx) x=ff[x][i]; 80 if(dep[ff[y][i]]>=dy) y=ff[y][i]; 81 }fx=x,fy=y; 82 return ans; 83 } 84 ll solve(int x,int y) 85 { 86 if(x==y) return f[x][0]+f[x][2]+a[x]; 87 else if(fa[x]==y) return f[x][0]+f[x][1]+f[y][2]+a[x]+a[y]-fe[x]; 88 else if(fa[y]==x) return f[y][0]+f[y][1]+f[x][2]+a[x]+a[y]-fe[y]; 89 int fx,fy,lca; 90 lca=Lca(x,y,fx,fy); 91 ll ans=0; 92 ans+=(vsum[x]+vsum[y]-vsum[lca]-vsum[fa[lca]])-(esum[x]+esum[y]-2ll*esum[lca]); 93 ans+=f[x][0]+f[y][0]; 94 if(y==fy&&x!=fx){ 95 ans+=g[x][1]+g[y][1]-2ll*g[lca][1]-f[fx][1]; 96 if(use[fx]) ans-=f[fx][0]+a[fx]-2ll*fe[fx]; 97 }else{ 98 ans+=g[x][1]+g[y][1]-2ll*g[lca][1]-f[fy][1]; 99 if(use[fy]) ans-=f[fy][0]+a[fy]-2ll*fe[fy]; 100 } 101 ans+=f[lca][2]; 102 return ans; 103 } 104 105 int main() 106 { 107 scanf("%d%d",&n,&q); 108 int x,y,w; 109 for(int i=1;i<=n;i++) 110 a[i]=gint(); 111 for(int i=1;i<n;i++) 112 x=gint(),y=gint(),w=gint(),ae(x,y,w),ae(y,x,w); 113 dep[1]=1; 114 vsum[1]=a[1],dfs1(1,-1); 115 g[1][0]=f[1][0],dfs2(1); 116 get_lca(); 117 for(int i=1;i<=q;i++) 118 x=gint(),y=gint(),printf("%lld ",solve(x,y)); 119 return 0; 120 }
以上是关于CF 1000G Two-Paths (树形DP)的主要内容,如果未能解决你的问题,请参考以下文章