luogu P1600 天天爱跑步

膜你赛T3考了这题的简化版,当场自闭。就此把此类题目总结一下。

膜你赛题意是这样的:相当于这个题最后问每个运动员被多少个观察员观察到。我们考虑把每条路径拆成向上和向下两条,本质相同,我们以向上路径举例。发现路径上满足 \(dep[S]-dep[x]=x\)\(S\) 表示路径起点) 的 \(x\) 都可以获得 \(1\) 的贡献。我们差分一下,最后再一遍 \(dfs\) 统计答案(离线)就行了。

这个题和膜你赛题的区别呢,就是此题是一个标记对根到它产生贡献,所以最后统计答案的时候是统计子树。现在有个非常骚的操作:统计差值。由于我们提前知道了我们要求的量是什么,我们可以在 \(dfs\) 开始的时候记录这个初值,在 \(dfs\) 结束时看看这个值现在长什么样子,最后取个差就是我们想要的贡献了。这个 trick 好像以前也见过,但因为记忆力低下考场上并没有想起来什么。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>

using namespace std;

const int N=300009;
int n,m,head[N],cnt,x[N],y[N],w[N],p[N],dep[N],f[N][30],L[N],tax[N*4],ans[N];
struct Edge
{
	int nxt,to;
}g[N*2];
vector <pair<int,int> > b[N];

void add(int from,int to)
{
	g[++cnt].nxt=head[from];
	g[cnt].to=to;
	head[from]=cnt;
}

void init()
{
	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",&w[i]);
	for (int i=1;i<=m;i++)
		scanf("%d %d",&x[i],&y[i]);
}

void dfs(int x,int fa)
{
	for (int i=head[x];i;i=g[i].nxt)
	{
		int v=g[i].to;
		if(v==fa)
			continue;
		dep[v]=dep[x]+1,f[v][0]=x;
		dfs(v,x);
	}
}

int LCA(int x,int y)
{
	if(dep[x]!=dep[y])
	{
		if(dep[x]<dep[y])
			swap(x,y);
		int k=dep[x]-dep[y];
		for (int i=20;i>=0;i--)
			if(k>=1<<i)
				k-=1<<i,x=f[x][i];
	}
	if(x==y) return x;
	for (int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}

void DFS(int x,int fa)
{
	int qwq=tax[p[x]];
	for (int i=0;i<b[x].size();i++)
		tax[b[x][i].first]+=b[x][i].second;
	for (int i=head[x];i;i=g[i].nxt)
	{
		int v=g[i].to;
		if(v==fa)
			continue;
		DFS(v,x);
	}
	ans[x]+=qwq-tax[p[x]];
}

void work()
{
	dfs(1,-1);
	for (int j=1;j<=20;j++)
		for (int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
//	for (int i=1;i<=n;i++,puts(""))
//		for (int j=i;j<=n;j++)
//			printf("%d ",LCA(i,j));
	for (int i=1;i<=m;i++)
		L[i]=LCA(x[i],y[i]);
	for (int i=1;i<=n;i++)
		p[i]=dep[i]+w[i];
	for (int i=1;i<=m;i++)
		b[x[i]].push_back(make_pair(dep[x[i]],-1)),
		b[f[L[i]][0]].push_back(make_pair(dep[x[i]],1));
	add(0,1);
	DFS(0,-1);
	for (int i=0;i<=n;i++)
		p[i]=w[i]-dep[i]+N,b[i].clear();
	for (int i=1;i<=m;i++)
	{
		int dis=dep[x[i]]-2*dep[L[i]]+N;
		b[y[i]].push_back(make_pair(dis,-1)),
		b[L[i]].push_back(make_pair(dis,1));
	}
	DFS(0,-1);
	for (int i=1;i<=n;i++)
		printf("%d ",ans[i]);puts("");
}

int main()
{
	init();
	work();
	return 0;
}

posted @ 2020-09-15 06:30  With_penguin  阅读(84)  评论(0编辑  收藏  举报