【题解】[NOIP2016 提高组]天天爱跑步
\(\text{Solution:}\)
被迫回来搞树论了)看自习找一个字符串看看)
大概题意:求多少路径满足每个点其对应 \(w[x]\) 是恰好路径上第 \(w[x]\) 个点。
稍微画一下图就会的……淦 没认真分析题目性质就看 sol 了,不应该啊……
首先一个特点:题目里面的上行和下行路径其实是不太一样的,这里需要我们分开处理。
先观察一下上行的路径,如果一个点在其上行路径上,那么其满足条件当且仅当 \(dep[x]+w[x]=dep[s].\) 这是因为从 \(s\) 点向上走 \(w[x]\) 步的 \(dep\) 也必然增加相同量,接下来如果 \(x\) 在其上行路径上,那必然于其深度相等。
再看看下行路径:观察如果满足条件,意味着当前点到路径终点的一段距离,即 \(dep[t]-dep[x]\) 应当等于路径长 \(dist-w[x].\) 这里是因为路径还剩下没有走的部分就应当恰好对应 \(x\) 到 \(t\) 的部分。
稍微画一下图就能理解的事……淦 大意了
那么,分别观察一下柿子,对于上行路径 \(dep[x]+w[x]=dep[s],\) 所以我们只需要维护对应深度 \(dep[s]\) 上的点有多少个,查询就直接用 \(dep[x]+w[x]\) 查询。
观察下行路径 \(dist-w[x]=dep[t]-dep[x]\to w[x]-dep[x]=dist-dep[t],\) 所以我们对于路径维护对应位置 \(dist-dep[t]\) 上面有多少点即可。
但是注意,路径走完了就不会再给别的点更新答案了。所以考虑一个树上差分。
先考虑一下我们如何维护上述所说的东西,显然的线段树合并扔上去即可。下面根据样例我们就能知道有些点算重了,而这个点往往是路径的 \(LCA.\)
考虑如何消去影响。一个满足条件的 \(LCA\) 对一条路径是会记录两遍的,我的思路是:对上下行路径分别维护线段树合并,在上行路径的 \(LCA\) 处 \(-1,\) 在下行路径的 \(fa[LCA]\) 处 \(-1.\)
这样,我们恰好让一个端点被计算了一次。
当时乱改以为是错的交上去没想到对了
哦对了,\(w[x]-dep[x]\) 是有可能出现负数的,所以整体平移一下即可。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
const int TN=5e6+10;
struct SGT{
int ls[TN],rs[TN],sum[TN],node;
inline void pushup(const int &x){sum[x]=sum[ls[x]]+sum[rs[x]];}
void change(int &x,const int &L,const int &R,const int &pos,const int &v){
if(!x)x=++node;
if(L==R){
sum[x]+=v;
return;
}
int mid=(L+R)>>1;
if(pos<=mid)change(ls[x],L,mid,pos,v);
else change(rs[x],mid+1,R,pos,v);
pushup(x);
}
int merge(const int &x,const int &y){
if(!x||!y)return x|y;
int p=++node;
sum[p]=sum[x]+sum[y];
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
return p;
}
int query(const int &x,const int &L,const int &R,const int &pos){
if(!x)return 0;
if(L==R)return sum[x];
int mid=(L+R)>>1;
if(pos<=mid)return query(ls[x],L,mid,pos);
else return query(rs[x],mid+1,R,pos);
}
}tr[2];
struct Path{
int s,t,l;
}p[N];
int n,m,head[N],tot,dep[N],pa[N];
int f[N][20],w[N],rt[N][2],ans[N];
struct E{int nxt,to;}e[N<<1];
inline void link(int x,int y){
e[++tot]=(E){head[x],y};
head[x]=tot;
}
void dfs(int x,int fa){
dep[x]=dep[fa]+1;
pa[x]=f[x][0]=fa;
for(int i=1;i<20;++i)f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==fa)continue;
dfs(j,x);
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=19;~i;--i)if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return x;
for(int i=19;~i;--i)if(f[x][i]&&f[y][i]&&f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return x==1?x:f[x][0];
}
void dfsmerge(int x){
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==pa[x])continue;
dfsmerge(j);
rt[x][0]=tr[0].merge(rt[x][0],rt[j][0]);
rt[x][1]=tr[1].merge(rt[x][1],rt[j][1]);
}
}
void dfsans(int x){
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==pa[x])continue;
dfsans(j);
}
if(dep[x]+w[x]<=n)ans[x]=tr[0].query(rt[x][0],1,n,dep[x]+w[x]);
ans[x]+=tr[1].query(rt[x][1],1,n<<1,w[x]-dep[x]+n);
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
link(u,v);link(v,u);
}
dfs(1,0);
for(int i=1;i<=n;++i)scanf("%d",&w[i]);
for(int i=1;i<=m;++i)scanf("%d%d",&p[i].s,&p[i].t);
for(int i=1;i<=m;++i)p[i].l=LCA(p[i].s,p[i].t);
for(int i=1;i<=m;++i){
int lens=dep[p[i].s]+dep[p[i].t]-(dep[p[i].l]<<1);
tr[0].change(rt[p[i].s][0],1,n,dep[p[i].s],1);
tr[1].change(rt[p[i].t][1],1,n<<1,lens-dep[p[i].t]+n,1);
tr[0].change(rt[f[p[i].l][0]][0],1,n,dep[p[i].s],-1);
tr[1].change(rt[p[i].l][1],1,n<<1,lens-dep[p[i].t]+n,-1);
}
dfsmerge(1);
dfsans(1);
// for(int i=1;i<=m;++i){if(w[p[i].l]==dep[p[i].s]-dep[p[i].l])ans[p[i].l]--;}
for(int i=1;i<=n;++i)printf("%d ",ans[i]);
return 0;
}