这题一看显然lca+树上差分,但是因为有w的限制不能直接加,所以考虑权值线段树合并,
每个选手的起点终点对于不同的节点的影响是不同的,这就非常麻烦了,但是可以发现无论如何他的深度是固定的,而对于一个节点i,能使他+1有如下两种情况:
1.dep[x]=dep[i]-w[i]
2.dep[x]=dep[i]+w[i]
于是对于一组s,t,求出他们的lca将路径分为两段,给s的dep[s]值加1,fa[lca]减1,t的2*dep[lca]-dep[s]加1,lca减1,
最后dfs线段树合并,对于一个节点x,查询对应线段树中dep[x]-w[x]+dep[x]+w[x]的个数,注意如果w[x]是0要除2(因为多加了一遍)。
有了“雨天的尾巴”的经验,这道题代码还是很好调的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cmath> using namespace std; struct edge { int u,v,nxt; #define u(x) ed[x].u #define v(x) ed[x].v #define n(x) ed[x].nxt }ed[300010]; int first[300010],num_e; #define f(x) first[x] int n,m,root; int dep[300010],du[300010]; struct tree { int ls,rs,l,r,sum; #define ls(x) tr[x].ls #define rs(x) tr[x].rs #define l(x) tr[x].l #define r(x) tr[x].r #define sum(x) tr[x].sum }tr[10000010]; int cnt,T[300010]; void add(int &x,int y,int z,int l,int r) { if(!x){x=++cnt;l(x)=l,r(x)=r;} if(l==r){sum(x)+=z;return;} int mid=(l(x)+r(x))>>1; if(y<=mid)add(ls(x),y,z,l,mid); else add(rs(x),y,z,mid+1,r); sum(x)=0; if(ls(x))sum(x)+=sum(ls(x)); if(rs(x))sum(x)+=sum(rs(x)); } int merge(int x,int y,int l,int r) { if(!x||!y)return x+y; if(l==r){sum(x)+=sum(y);return x;} int mid=(l+r)>>1; ls(x)=merge(ls(x),ls(y),l,mid); rs(x)=merge(rs(x),rs(y),mid+1,r); sum(x)=sum(ls(x))+sum(rs(x)); return x; } int ask(int x,int val,int l,int r) { if(l==r){return sum(x);} int mid=(l+r)>>1; if(val<=mid)return ask(ls(x),val,l,mid); else return ask(rs(x),val,mid+1,r); } int f[300010][21]; int LCA(int x,int y) { if(x==y)return x; if(dep[x]>dep[y])swap(x,y); while(dep[x]<dep[y]) for(int i=0;;i++) if(dep[f[y][i]]<dep[x]) {y=f[y][i-1];break;} if(x==y)return x; while(f[x][0]!=f[y][0]) for(int i=0;;i++) if(f[x][i]==f[y][i]) {x=f[x][i-1],y=f[y][i-1];break;} return f[x][0]; } void dfs(int x,int de,int fa) { dep[x]=de;f[x][0]=fa; for(int i=f(x);i;i=n(i)) dfs(v(i),de+1,x); } int ans[300010],w[300010]; void dfs2(int x) { for(int i=f(x);i;i=n(i)) { dfs2(v(i)); T[x]=merge(T[x],T[v(i)],-n-1,n+1); } int t1=ask(T[x],dep[x]+w[x],-n-1,n+1),t2=ask(T[x],dep[x]-w[x],-n-1,n+1); ans[x]=t1+t2; if(!w[x])ans[x]-=t1; } inline void add_e(int u,int v); signed main() { // freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); int u,v; for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); add_e(u,v);du[v]++; } for(int i=1;i<=n;i++)scanf("%d",&w[i]); for(int i=1;i<=n;i++) if(!du[i]){root=i;break;} dfs(root,1,0); for(int i=1;i<=20;i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1]; int s,t; for(int i=1;i<=m;i++) { scanf("%d%d",&s,&t); int lca=LCA(s,t); add(T[s],dep[s],1,-n-1,n+1); if(lca!=root){add(T[f[lca][0]],dep[s],-1,-n-1,n+1);} add(T[t],2*dep[lca]-dep[s],1,-n-1,n+1); add(T[lca],2*dep[lca]-dep[s],-1,-n-1,n+1); } dfs2(root); printf("%d",ans[1]); for(int i=2;i<=n;i++) printf(" %d",ans[i]); } inline void add_e(int u,int v) { ++num_e; u(num_e)=u; v(num_e)=v; n(num_e)=f(u); f(u)=num_e; }