【NOIp2016】天天爱跑步
题面
https://www.luogu.org/problem/P1600
题解
树链剖分$+$对每条重链分治$+$桶。
注意:树链剖分求$LCA$,跳矮(重链顶端的高矮)返高。
#include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ri register int #define N 300000 using namespace std; inline int read() { int ret=0,f=0;char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0' && ch<='9') ret*=10,ret+=ch-'0',ch=getchar(); return f?-ret:ret; } int siz[N],top[N],p[N],cnt,dep[N],fa[N],ans[N]; vector<int> to[N]; int n,m,w[N]; void dfs_1(int x,int ff) { fa[x]=ff; dep[x]=dep[ff]+1; siz[x]=1; for (ri i=0;i<to[x].size();i++) { int y=to[x][i]; if (y==ff) continue; dfs_1(y,x); siz[x]+=siz[y]; } } void dfs_2(int x,int ff,int ctop) { int md=0,hs=-1; p[x]=++cnt; top[x]=ctop; for (ri i=0;i<to[x].size();i++) { int y=to[x][i]; if (y==ff) continue; if (siz[y]>md) md=siz[y],hs=y; } if (hs!=-1) dfs_2(hs,x,ctop); for (ri i=0;i<to[x].size();i++) { int y=to[x][i]; if (y==ff || y==hs) continue; dfs_2(y,x,y); } } int lca(int x,int y) { while (top[x]^top[y]) { if (dep[top[x]]>dep[top[y]]) x=fa[top[x]]; else y=fa[top[y]]; } if (dep[x]<dep[y]) return x; else return y; } struct node { int v,p,top,id; bool operator < (const node &rhs) const { if (top!=rhs.top) return top<rhs.top; if (v!=rhs.v) return v<rhs.v; if (p!=rhs.p) return p<rhs.p; return 0; } } up[N],dw[N]; int sup[N],sdw[N]; void count_up(int t,int lp,int rp,int v) { int a=lower_bound(up+1,up+n+1,(node){v,lp,t,-1})-up; int b=upper_bound(up+1,up+n+1,(node){v,rp,t,-1})-up; sup[a]++; sup[b]--; } void count_dw(int t,int lp,int rp,int v) { int a=lower_bound(dw+1,dw+n+1,(node){v,lp,t,-1})-dw; int b=upper_bound(dw+1,dw+n+1,(node){v,rp,t,-1})-dw; sdw[a]++; sdw[b]--; } int main() { n=read(); m=read(); for (ri i=1;i<n;i++) { int u=read(),v=read(); to[u].push_back(v); to[v].push_back(u); } dfs_1(1,0); dfs_2(1,0,1); for (ri i=1;i<=n;i++) w[i]=read(); for (ri i=1;i<=n;i++) { up[p[i]]=(node){dep[i]+w[i],p[i],top[i],i}; dw[p[i]]=(node){dep[i]-w[i],p[i],top[i],i}; } sort(up+1,up+n+1); sort(dw+1,dw+n+1); for (ri i=1;i<=m;i++) { int s=read(),t=read(); int x=lca(s,t); int vid=dep[s],vid2=dep[t]-(dep[s]+dep[t]-2*dep[x]); while (1) { if (dep[top[s]]>dep[x]) count_up(top[s],p[top[s]],p[s],vid),s=fa[top[s]]; else {count_up(top[s],p[x],p[s],vid); break;} } while (1) { if (dep[top[t]]>dep[x]+1) count_dw(top[t],p[top[t]],p[t],vid2),t=fa[top[t]]; else {count_dw(top[t],p[t]-(dep[t]-(dep[x]+1)),p[t],vid2); break;} } } for (ri i=1;i<=n;i++) { sup[i]+=sup[i-1]; ans[up[i].id]+=sup[i]; sdw[i]+=sdw[i-1]; ans[dw[i].id]+=sdw[i]; } for (ri i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }