CF1000G Two-Paths

Posted lnxcj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1000G Two-Paths相关的知识,希望对你有一定的参考价值。

G. Two-Paths
time limit per test
3.5 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a weighted tree (undirected connected graph with no cycles, loops or multiple edges) with nn vertices. The edge {uj,vj}{uj,vj} has weight wjwj. Also each vertex ii has its own value aiai assigned to it.

Let‘s call a path starting in vertex uu and ending in vertex vv, where each edge can appear no more than twice (regardless of direction), a 2-path. Vertices can appear in the 2-path multiple times (even start and end vertices).

For some 2-path pp profit Pr(p)=vdistinct vertices in pavedistinct edges in pkewePr(p)=∑v∈distinct vertices in pav−∑e∈distinct edges in pke⋅we, where keke is the number of times edge ee appears in pp. That is, vertices are counted once, but edges are counted the number of times they appear in pp.

You are about to answer mm queries. Each query is a pair of vertices (qu,qv)(qu,qv). For each query find 2-path pp from ququ to qvqv with maximal profit Pr(p)Pr(p).

Input

The first line contains two integers nn and qq (2n31052≤n≤3⋅105, 1q41051≤q≤4⋅105) — the number of vertices in the tree and the number of queries.

The second line contains nn space-separated integers a1,a2,,ana1,a2,…,an (1ai109)(1≤ai≤109) — the values of the vertices.

Next n1n−1 lines contain descriptions of edges: each line contains three space separated integers uiui, vivi and wiwi (1ui,vin1≤ui,vi≤n, uiviui≠vi, 1wi1091≤wi≤109) — there is edge {ui,vi}{ui,vi} with weight wiwi in the tree.

Next qq lines contain queries (one per line). Each query contains two integers quiqui and qviqvi (1qui,qvin)(1≤qui,qvi≤n) — endpoints of the 2-path you need to find.

Output

For each query print one integer per line — maximal profit Pr(p)Pr(p) of the some 2-path pp with the corresponding endpoints.

Solution

对于x到y的路径上的每条边很显然只能走一次,我们要考虑的是路径上的点向外走一个Two-Path可以获得的最大权值

用树形dp进行预处理

分析一下可以发现,对一个节点的贡献可以来自祖先,后代,和兄弟(即其后代)

技术分享图片

所以分三个dp来解决

对于当前u,子树对其贡献(dp1)不必多说

兄弟对其贡献(dp3)是其父节点的dp1-u对父节点的贡献

祖先对其贡献(dp3)是u到祖先这条链及其这条链的分支到兄弟节点可以获得的最大权值(均可以看做上对下的贡献)

预处理出这些之后不难想到统计答案的方式

注意,在对沿路上的节点统计兄弟的贡献时不要忘了lca的子节点的兄弟贡献不能加重

用倍增lca可以找到lca到x,y的链上的子节点

  1 //xcj is handsome
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 int n,q,cnt,tot;
  7 long long ans;
  8 int fa[300005][21];
  9 int head[300005];
 10 int dep[300005];
 11 int cnn[300005];
 12 long long sum[300005];
 13 long long bro[300005];
 14 long long val[300005];
 15 long long dis[300005];
 16 long long dp1[300005];
 17 long long dp2[300005];
 18 long long dp3[300005];
 19 struct Edge{
 20     int fr;
 21     int to;
 22     long long val;
 23     int nxt;
 24 }edge[600005];
 25 void init(){
 26     dep[1]=1;
 27     memset(head,-1,sizeof(head));
 28 }
 29 void addedge(int f,int t,long long v){
 30     cnt++;
 31     edge[cnt].fr=f;
 32     edge[cnt].to=t;
 33     edge[cnt].val=v;
 34     edge[cnt].nxt=head[f];
 35     head[f]=cnt;
 36 }
 37 void dfs1(int u){
 38     sum[u]=sum[fa[u][0]]+val[u];
 39     for(int i=1;i<=20;i++){
 40         fa[u][i]=fa[fa[u][i-1]][i-1];
 41     }
 42     for(int i=head[u];i!=-1;i=edge[i].nxt){
 43         int v=edge[i].to;
 44         if(v==fa[u][0])continue;
 45         cnn[v]=i;
 46         fa[v][0]=u;dep[v]=dep[u]+1;
 47         dis[v]=dis[u]+edge[i].val;
 48         dfs1(v);
 49         dp1[u]+=max(dp1[v]+val[v]-edge[i].val*2,0ll);
 50     }
 51     for(int i=head[u];i!=-1;i=edge[i].nxt){
 52         int v=edge[i].to;
 53         if(v==fa[u][0])continue;
 54         dp2[v]=dp1[u]-max(dp1[v]+val[v]-edge[i].val*2,0ll);
 55     }
 56 }
 57 void dfs2(int u){
 58     bro[u]=bro[fa[u][0]]+dp2[u];
 59     for(int i=head[u];i!=-1;i=edge[i].nxt){
 60         int v=edge[i].to;
 61         if(v==fa[u][0])continue;
 62         dp3[v]=max(dp3[u]+val[u]-edge[i].val*2+dp2[v],0ll);
 63         dfs2(v);
 64     }
 65 }
 66 int lca(int x,int y){
 67     if(dep[y]>dep[x]){
 68         swap(x,y);
 69     }
 70     for(int i=20;i>=0;i--){
 71         if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
 72     }
 73     if(x==y)return x;
 74     int ret;
 75     for(int i=20;i>=0;i--){
 76         if(fa[x][i]!=fa[y][i]){
 77             x=fa[x][i];
 78             y=fa[y][i];
 79         }else{
 80             ret=fa[x][i];
 81         }
 82     }
 83     return ret;
 84 }
 85 int main(){
 86     init();
 87     scanf("%d%d",&n,&q);
 88     for(int i=1;i<=n;i++){
 89         scanf("%I64d",&val[i]);
 90     }
 91     for(int i=1;i<n;i++){
 92         int u,v,w;
 93         scanf("%d%d%d",&u,&v,&w);
 94         addedge(u,v,(long long)w);
 95         addedge(v,u,(long long)w);
 96     }
 97     dfs1(1);
 98     dfs2(1);
 99     for(int i=1;i<=q;i++){
100         int u,v,f;ans=0;
101         scanf("%d%d",&u,&v);
102         f=lca(u,v);
103         ans-=dis[u]+dis[v]-2*dis[f];
104         ans+=sum[u]+sum[v]-sum[f]-sum[fa[f][0]];
105         if(dep[u]>dep[v])swap(u,v);
106         if(u==f){
107             ans+=dp3[u]+dp1[v];
108             ans+=bro[v]-bro[u];
109         }else{
110             ans+=dp3[f]+dp1[u]+dp1[v];
111             ans+=bro[u]+bro[v];
112             for(int i=20;i>=0;i--){
113                 if(dep[fa[u][i]]>dep[f])u=fa[u][i];
114                 if(dep[fa[v][i]]>dep[f])v=fa[v][i];
115             }
116             ans-=bro[u]+bro[v];
117             ans+=dp2[u];
118             ans-=max(dp1[v]+val[v]-2*edge[cnn[v]].val,0ll);
119         }
120         printf("%I64d
",ans);
121     }
122     return 0;
123 }

 

以上是关于CF1000G Two-Paths的主要内容,如果未能解决你的问题,请参考以下文章

CF 1000G Two-Paths (树形DP)

sh 获得1000G亚群的等位基因频率

如何从后台弹出片段

业余草分享100套精选1000G架构师资料课程(超1T的IT学习资料免费送)

花了 1000G,我终于弄清楚了 Serverless 是什么(上)?

cf 模拟