洛谷P1600 天天爱跑步
将每个玩家的路径看作两部分:向根上行以及向叶子下行
p为每个点出现观察员的时间。子树中的起点u到当前点v的路径即是上行,如果dep[v]+p[v]=dep[u],则u对v产生一个贡献。当前点u到子树中终点v的路径即是下行,如果dep[v]-dis(x,v)=dep[u]-p[u](x为这条路径的起点,v为终点,dis(x,v)为两点的距离),则v对u产生一个贡献。
树上差分,将所有路径标记到树上。开两个桶分别存所有起点的dep[x]和所有终点的dep[x]-dis,在起点和终点处使贡献+1,在lca处把它们的贡献分别-1,表示出了这棵子树以后不再产生贡献。统计答案的时候,如果一条路径对lca产生了贡献,那么起点和终点都会算一次,因此lca处将贡献-1的时候要判断是否被这条路径贡献了答案。
查询某棵子树中点的答案的时候,有可能其它子树的贡献仍然留在桶内。由于这道题只是记录贡献的累计,因此在dfs到达某个点的时候记录一次答案,将要出这个点的时候再查询一次答案,减去第一次记录的值使差值成为最终答案。
统计某个点的答案时,先把所有+1的贡献扔进桶里,进行查询,再处理所有贡献-1的操作。
#include<iostream> #include<cstdio> #include<vector> using namespace std; const int N=300010,M=300010; int n,m,a[N]; int ver[2*N],Next[2*N],head[N],tot; int dep[N],ans[2][N]; int c[2][600010]; struct node{ int opt,val,x,dis; node(int a=0,int b=0,int c=0,int d=0){ opt=a,val=b,x=c,dis=d; } }; vector<node>s[N]; void add(int x,int y){ ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } int st[N][21]; void dfs(int x,int fa){ for(int i=1;i<=20;i++){ st[x][i]=st[st[x][i-1]][i-1]; if(!st[x][i])break; } for(int i=head[x];i;i=Next[i]){ int y=ver[i]; if(y==fa)continue; dep[y]=dep[x]+1; st[y][0]=x; dfs(y,x); } } int get(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=20;i>=0;i--){ if(st[x][i]&&dep[st[x][i]]>=dep[y])x=st[x][i]; } if(x==y)return x; for(int i=20;i>=0;i--){ if(st[x][i]!=st[y][i]){ x=st[x][i],y=st[y][i]; } } return st[x][0]; } void dfs1(int x,int fa){ ans[0][x]=c[0][dep[x]+a[x]]; ans[1][x]=c[1][dep[x]-a[x]+N]; for(int i=head[x];i;i=Next[i]){ int y=ver[i]; if(y==fa)continue; dfs1(y,x); } for(int i=0;i<s[x].size();i++){ int v=s[x][i].x,val=s[x][i].val,opt=s[x][i].opt,dis=s[x][i].dis; if(val==-1)continue; if(opt==0){ c[0][dep[v]]+=val; } else{ c[1][dep[v]-dis+N]+=val; } } ans[0][x]=c[0][dep[x]+a[x]]-ans[0][x]; ans[1][x]=c[1][dep[x]-a[x]+N]-ans[1][x]; for(int i=0;i<s[x].size();i++){ int v=s[x][i].x,val=s[x][i].val,opt=s[x][i].opt,dis=s[x][i].dis; if(val==1)continue; if(opt==0){ c[0][dep[v]]+=val; if(dep[x]+a[x]==dep[v])ans[0][x]--; } else{ c[1][dep[v]-dis+N]+=val; } } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<n;i++){ scanf("%d%d",&x,&y); add(x,y),add(y,x); } for(int i=1;i<=n;i++)scanf("%d",&a[i]); dfs(1,0); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); int lca=get(x,y); int dis=dep[x]+dep[y]-2*dep[lca]; s[x].push_back(node(0,1,x,0)); s[y].push_back(node(1,1,y,dis)); s[lca].push_back(node(0,-1,x,0)); s[lca].push_back(node(1,-1,y,dis)); } dfs1(1,0); for(int i=1;i<=n;i++){ printf("%d ",ans[0][i]+ans[1][i]); } return 0; }