天天爱跑步 洛谷1600 NOIP2016 树上差分 LCA

题目链接

题意好像不是很容易说明白,自己看题目吧。

题解:
如今再做往年NOIP题目还是不怎么会啊。这题想了想没想出来,然后看了好多题解,又对着y_immortal大神的代码研究了好久,才有些明白,水平还是不行啊。
首先我们把无根树转化为以1为根的有根树,dfs一边求出每个点的深度和他的倍增父节点数组。我们把一条路径看成两部分,一部分是向起点与终点的LCA走的,一部分是向下走的。我们发现,对于向上走的那一部分,在x这个点能看到的点应该满足 d e p [ x ] + w [ x ] = d e p [ i ] dep[x]+w[x]=dep[i] dep[x]+w[x]=dep[i],而向下走的情况如果想在x被看到应该满足 d e p [ x ] − w [ i ] = d e p [ i ] dep[x]-w[i]=dep[i] dep[x]w[i]=dep[i],其中i为x子树中的节点。我们用一个桶来存 d e p [ i ] dep[i] dep[i],然后对于每个点算它子树内 d e p [ x ] + w [ x ] dep[x]+w[x] dep[x]+w[x] d e p [ x ] − w [ x ] dep[x]-w[x] dep[x]w[x]的数量,求法是减去进入子树之前的数量,然后加上遍历完子树之后的数量,就是子树的答案了。要特判路径一段是两个端点的LCA的情况,因为LCA会被重复计算。向上和向下的式子不太一样,画个图就能推出来。可能讲得确实不太清楚,我自己有些地方也不是很明白。

#include 
using namespace std;

int n,m,f[300010][20],hed[600010],cnt,num[3000010],ans[600010];
vector<int> adds[300010],addx[300010],dels[300010],delx[300010];
int w[600010],dep[600010],vis[600010];
const int gg=600005;
struct node
{
	int to,next;
}a[600010];
void add(int from,int to)
{
	a[++cnt].to=to;
	a[cnt].next=hed[from];
	hed[from]=cnt;
}
void dfs(int x,int fa)
{
	f[x][0]=fa;
	for(int i=1;i<=19;++i)
	f[x][i]=f[f[x][i-1]][i-1];	
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(y!=fa)
		{
			dep[y]=dep[x]+1;
			dfs(y,x);
		}
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])
	swap(x,y);
	for(int i=19;i>=0;--i)
	{
		if(dep[f[x][i]]>=dep[y])
		x=f[x][i];
	}
	if(x==y)
	return x;
	for(int i=19;i>=0;--i)
	{
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}
void querys(int x)
{
	ans[x]-=num[dep[x]+w[x]+gg];
	vis[x]=1;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(!vis[y])
		querys(y);
	}
	for(int i=0;i<adds[x].size();++i)
	num[adds[x][i]]++;
	ans[x]+=num[dep[x]+w[x]+gg];
	for(int i=0;i<dels[x].size();++i)
	num[dels[x][i]]--;
}
void queryx(int x)
{
	ans[x]-=num[dep[x]-w[x]+gg];
	vis[x]=1;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(!vis[y])
		queryx(y);
	}
	for(int i=0;i<addx[x].size();++i)
	num[addx[x][i]]++;
	ans[x]+=num[dep[x]-w[x]+gg];
	for(int i=0;i<delx[x].size();++i)
	num[delx[x][i]]--;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n-1;++i)
	{
		int x,y;;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;++i)
	scanf("%d",&w[i]);
	dep[1]=1;
	dfs(1,0);
	for(int i=1;i<=m;++i)
	{
		int s,t,l;
		scanf("%d%d",&s,&t);
		l=lca(s,t);
		if(l==s)
		{
			addx[t].push_back(dep[s]+gg);
			delx[s].push_back(dep[s]+gg);
		}
		else if(l==t)
		{
			adds[s].push_back(dep[s]+gg);
			dels[t].push_back(dep[s]+gg);
		}
		else
		{
			addx[t].push_back(2*dep[l]-dep[s]+gg);
			delx[l].push_back(2*dep[l]-dep[s]+gg);
			adds[s].push_back(dep[s]+gg);
			dels[l].push_back(dep[s]+gg);
			if(dep[s]-dep[l]==w[l])
			ans[l]--;
		}
	}
	querys(1);
	memset(vis,0,sizeof(vis));
	queryx(1);
	for(int i=1;i<=n;++i)
	printf("%d ",ans[i]);
	printf("\n");
	return 0;
}

你可能感兴趣的:(树上差分,LCA)