【题解】[NOIP2016 提高组]天天爱跑步

[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;
}
posted @ 2021-08-26 12:58  Refined_heart  阅读(34)  评论(0编辑  收藏  举报