洛谷上这道题居然有LCT的标签......真是吓人。
实际上就是个LCA加上树上乱搞就行了。
这道题难就难在思维,实际上编程实现的难度很小,尤其是找好方法之后。
本人使用倍增LCA代码共2K,没怎么压行。
第一眼一看,每个玩家的路线都是s到t的链。
按照古老的套路,就是把 [ s 到 t ] 拆成 [ s 到根 ] + [ t 到根 ] - [ lca 到根 ] - [ lca的父亲到根 ] 四条链。
简单起见,我们把 [ lca的父亲到根 ] 也变成 [ lca到根 ] 。
这样每个点都对且只对他的祖先产生贡献。
我们统计每个点子树的贡献和就好了。
有个小bug:
在统计的时候,我们统计的是子树的贡献和,并不包含该节点。
所以如果在 s、t 和 lca 上打标记,只会影响他们的祖先节点的结果,对他们自身没有影响。
所以我们特判 s、t 和 lca 的值。
如果产生贡献(可正可负),就在统计之前先加上再说。
这样就能统计每个点的答案啦~
是不是还差了一点什么......
是否产生贡献还与时间有关。
我们设i的出发时间为st[i],点p的深度为d[p],w[i]的意义如题面所示。
对于向上走的(s到根,两条lca到根之一),若在 p 处产生贡献,则有:start_time + d [ s ] - d [ p ] = w [ p ]
移项,得:start_time + d [ s ] = w [ p ] + d [ p ]
这样等式右边只与 p 有关。
我们可以建一个数组 h ,使 h [ start_time + d [ s ] ] ++(或--)(取决于贡献是1还是-1)
实际上对于上行的所有链,start_time = 0
我们查询一个点 p 的时候,h [ w [ p ] + d [ p ] ] 是多少,ans [ p ] 就+=多少。
下行也是类似的做法。
start_time - d [ lca ] = w [ p ] - d [ p ]
其中start_time = d [ s ] - d [ lca ]
标记时h [ start_time - d [ lca ] ] ++(或--)
查询时 ans [ p ] += h [ w [ p ] - d [ p ] ]
一个点只对祖先产生贡献,所以我们不能直接改变 h 数组。
我们在dfs的时候,每到一个点,把关于该点的标记全部加到 h 里,再dfs该点的子节点。
dfs子节点前后答案的差值,就是子树内的贡献。
关于一个点的标记可能有很多,使用类似链式前向星的方式记录下来。
最后按点标号输出 ans [ i ] 。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n,m; 7 int hd[300005],nx[600005],to[600005],cnt; 8 int w[300005]; 9 10 void add(int af,int at) 11 { 12 to[++cnt]=at; 13 nx[cnt]=hd[af]; 14 hd[af]=cnt; 15 } 16 17 int d[300005],f[300005][25]; 18 19 void pre(int p,int fa) 20 { 21 f[p][0]=fa; 22 d[p]=d[fa]+1; 23 for(int i=hd[p];i;i=nx[i]) 24 { 25 if(to[i]!=fa)pre(to[i],p); 26 } 27 } 28 29 int lca(int x,int y) 30 { 31 if(d[x]<d[y])swap(x,y); 32 for(int i=20;i>=0;i--) 33 { 34 if(d[f[x][i]]>=d[y])x=f[x][i]; 35 } 36 if(x==y)return x; 37 for(int i=20;i>=0;i--) 38 { 39 if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 40 } 41 return f[x][0]; 42 } 43 44 int ct[2]; 45 int st[300005][2],nxt[600005][2],k[600005][2],val[600005][2]; 46 int ans[300005]; 47 48 void mark(int p,int key,int v,int dir) 49 { 50 val[++ct[dir]][dir]=v; 51 k[ct[dir]][dir]=key; 52 nxt[ct[dir]][dir]=st[p][dir]; 53 st[p][dir]=ct[dir]; 54 } 55 56 int h[900005][2]; 57 58 void dfs(int p,int fa) 59 { 60 for(int no=0;no<=1;no++) 61 for(int i=st[p][no];i;i=nxt[i][no]) 62 h[k[i][no]+n][no]+=val[i][no]; 63 int tmp=(h[w[p]+d[p]+n][0]+h[w[p]-d[p]+n][1]); 64 for(int i=hd[p];i;i=nx[i])if(to[i]!=fa)dfs(to[i],p); 65 ans[p]+=(h[w[p]+d[p]+n][0]+h[w[p]-d[p]+n][1]-tmp); 66 } 67 68 int main() 69 { 70 scanf("%d%d",&n,&m); 71 for(int i=1;i<n;i++) 72 { 73 int aa,bb; 74 scanf("%d%d",&aa,&bb); 75 add(aa,bb); 76 add(bb,aa); 77 } 78 for(int i=1;i<=n;i++)scanf("%d",&w[i]); 79 pre(1,0); 80 for(int i=1;i<=20;i++) 81 { 82 for(int j=1;j<=n;j++) 83 { 84 f[j][i]=f[f[j][i-1]][i-1]; 85 } 86 } 87 cnt=0; 88 for(int i=1;i<=m;i++) 89 { 90 int s,t; 91 scanf("%d%d",&s,&t); 92 int l=lca(s,t); 93 mark(s,d[s],1,0); 94 mark(l,d[s],-1,0); 95 mark(t,d[s]-d[l]*2,1,1); 96 mark(l,d[s]-d[l]*2,-1,1); 97 if(w[l]==d[s]-d[l])ans[l]--; 98 if(w[s]==0)ans[s]++; 99 if(w[t]==d[s]+d[t]-d[l]*2)ans[t]++; 100 } 101 dfs(1,0); 102 for(int i=1;i<=n;i++)printf("%d ",ans[i]); 103 return 0; 104 }