天天爱跑步
一道:树上差分+LCA+桶的题
说实话,这道题放在D1T2就是非常不合理的。然而CCF就是放了,并且还是能依靠CSP捞钱,你也必须交钱参加比赛。这个社会是多么的不公啊!闲扯结束
显然如果对每条路径都进行一次处理,复杂度不对。考虑对路径进行一次预处理,然后进行统一的计算答案。我们发现当一条路径对某一个点P产生贡献时满足这个条件:
1.当\(P\)在该路径的\(S\)与\(LCA\)之间时 \(dep[S]-dep[P]=watch[P]\) 移一下项为\(dep[S]=dep[P]+watch[P]\)那么该路径起点对P有贡献
2.当\(P\)在该路径的\(LCA\)与\(T\)之间时 \(dist[S,T]-watch[P]=dep[T]-dep[P]\) 移一下项\(dist[S,T]-dep[T]=watch[P]-dep[P]\)那么该路径终点对P有贡献
然后用一个全局桶维护这些贡献 当枚举到当前点的时候 将当前点作为终点和起点的贡献加在桶里面。然后计算当前点的ans
细节:一个它只会受其子树的贡献,其他的贡献不会有的。如何处理?在开始遍历这个点的时候用\(ad1,ad2\)来存储已经存在的桶里面的值 然后遍历子树 当又返回到当前节点的时候 计算当前节点的答案。显然当前节点的答案为\(ans[x]+=bas1[watch[x]+dep[x]]-ad1+bas2[watch[x]-dep[x]+maxn]-ad2\)
注意到我们对\(bas2\)的计算的时候加了一个\(maxn\) ,其实原因很显然 \(watch[x]-dep[x]\)有可能小于零
还有就是当遍历完当前点 应该将以当前点为LCA的路径的影响全部消除。因为如果以当前点为LCA,显然不会对当前点的父亲及祖先产生影响,应该减掉
然后就是代码了QAQ 我是用的邻接表存储的以当前节点为终点的路径编号,以及以当前节点为LCA的路径编号
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define int long long using namespace std; const int maxn=500010; int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1,watch[maxn],fa[maxn][25]; int st[maxn],ed[maxn],stt[maxn],dist[maxn],ans[maxn],dep[maxn]; int fir1[maxn],nxt1[maxn<<1],to1[maxn<<1],tot1,fir2[maxn],nxt2[maxn<<1],to2[maxn<<1],tot2; void add(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;} void dfs1(int x){ dep[x]=dep[fa[x][0]]+1; for(int i=0;i<=20;i++) fa[x][i+1]=fa[fa[x][i]][i]; for(int i=fir[x];i;i=nxt[i]){ int y=to[i];if(y==fa[x][0]) continue; fa[y][0]=x;dfs1(y); } } int LCA(int x,int y){ if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--){ if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; if(x==y) return x; } for(int i=20;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ x=fa[x][i];y=fa[y][i]; } } return fa[x][0]; } void add1(int x,int y){nxt1[++tot1]=fir1[x];fir1[x]=tot1;to1[tot1]=y;} void add2(int x,int y){nxt2[++tot2]=fir2[x];fir2[x]=tot2;to2[tot2]=y;} int bas1[maxn<<1],bas2[maxn<<1],ad1,ad2; void dfs2(int x){ ad1=bas1[watch[x]+dep[x]];ad2=bas2[watch[x]-dep[x]+maxn]; for(int i=fir[x];i;i=nxt[i]){ int y=to[i];if(y==fa[x][0]) continue; dfs2(y); } bas1[dep[x]]+=stt[x]; for(int i=fir1[x];i;i=nxt1[i]){ int y=to1[i]; bas2[dist[y]-dep[ed[y]]+maxn]++; } ans[x]+=bas1[watch[x]+dep[x]]-ad1+bas2[watch[x]-dep[x]+maxn]-ad2; for(int i=fir2[x];i;i=nxt2[i]){ int y=to2[i]; bas1[dep[st[y]]]--; bas2[dist[y]-dep[ed[y]]+maxn]--; } } signed main(){ n=read();m=read(); for(int i=1,x,y;i<=n-1;i++){ x=read();y=read(); add(x,y);add(y,x); } for(int i=1;i<=n;i++) watch[i]=read(); dep[1]=1; dfs1(1); for(int i=1,x,y,lca;i<=m;i++){ x=read();y=read(); lca=LCA(x,y);st[i]=x;ed[i]=y; dist[i]=dep[x]+dep[y]-2*dep[lca]; stt[x]++;add1(y,i);add2(lca,i); if(dep[lca]+watch[lca]==dep[x]) ans[lca]--; } dfs2(1); for(int i=1;i<=n;i++) printf("%lld ",ans[i]); }