不还就跑路

题意

给定一棵树,一人从树上一个点出发准备从某个叶子处逃出,你要部署一些守卫从叶子出发抓住此人,所有人速度相同,求从每个点出发,至少部署多少守卫保证捉住此人。

思路

先考虑从一个点出发,把这个点当作根。由于逃跑的人可以任意抉择路线,因此所有守卫往上走遇到逃跑的人的时候所在的点的子树应该覆盖所有叶子。

形式化地,设 \(g[x]\)\(x\) 到最近的一个叶子的距离,\(d[x]\)\(x\) 的深度,如果一个点 \(x\) 满足 \(g[x]\leqslant d[x]\)(守卫比逃跑者更早到 \(x\) 点),那么只需要这个点的某个叶子放一个守卫即可至少把这个点的子树守住(可能可以守住更多)。

而最优方案显然是在每一个极大的,只需要一个守卫的子树个数。

如果只是计算一个点的答案,已经足够了。但如果是要算所有点,那么”极大“这个限制必须要转换一下,方便计数。

考虑构造一种权值 \(w[x]\),使得每一个子树的权值和都为 \(1\),即满足 \(\forall u,\sum\limits_{v\in \operatorname{tree}(u)}w[v]=1\),其中 \(\operatorname{tree}(u)\) 表示以 \(u\) 为根的子树的点集。

差分可得 \(w[u]=1-\sum\limits_{v\in \operatorname{son}(u)}\sum\limits_{k\in \operatorname{tree}(v)}w[k]\)

带入上述限制得 \(w[u]=1-\sum\limits_{v\in \operatorname{son}(u)}1\)

对于无根树,得到一个更普遍的形式 \(w[u]=2-\mathit{deg}[u]\)

于是点 \(x\) 的答案可表示为 \(\sum\limits_{i=1}^n [g[i]\leqslant\operatorname{dis}(i,x)]w[i]\)

任两点之间都有贡献,考虑点分治。

设当前重心为 \(p\)\(d[i]\)\(i\)\(p\) 的距离,那么:

\([g[i]\leqslant \operatorname{dis}(x,i)]=[g[i]\leqslant d[x]+d[i]]=[g[i]-d[i]\leqslant d[x]]\)

于是每一轮点分,把 \(w[i]\)\(g[i]-d[i]\) 排序,一次查询就是一次二分获取前缀和。

时间复杂度 \(\mathcal{O}(n\log^2n)\)

代码

点击查看代码
#include<bits/stdc++.h>
#define R register int
#define I inline
#define ll long long
using namespace std;
const int N=7e4+3,inf=2e9;
struct edge{int to,nxt;}e[N<<1];
int lst[N],f[N],g[N],tt;
I void link(int u,int v)
{
	e[++tt].to=v;
	e[tt].nxt=lst[u];
	lst[u]=tt;
}
int q[N],hd,tl;
void bfs()
{
	while(hd<tl)
	{
		int u=q[hd++];
		for(R i=lst[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(g[u]+1<g[v])g[v]=g[u]+1,q[tl++]=v;
		}
	}
}
int sz[N],mx[N],rt,tot;
bool vs[N];
void dfsz(int u,int F)
{
	sz[u]=1;mx[u]=0;
	for(R i=lst[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v!=F&&!vs[v])
		{
			dfsz(v,u);
			sz[u]+=sz[v];
			mx[u]=max(mx[u],sz[v]);
		}
	}
	mx[u]=max(mx[u],tot-sz[u]);
	if(mx[rt]>mx[u])rt=u;
}
int grt(int u,int t)
{
	rt=0;tot=t;
	dfsz(u,0);
	return rt;
}
int w[N],d[N],s[N],in[N],out[N],a[N],ret[N],ti;
I bool cmp(int x,int y){return g[x]-d[x]<g[y]-d[y];}
void dfsd(int u,int F)
{
	in[u]=++ti;a[ti]=u;
	for(R i=lst[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v!=F&&!vs[v])
		{
			d[v]=d[u]+1;
			dfsd(v,u);
		}
	}
	out[u]=ti+1;
}
void calc(int l,int r,int k)
{
	int n=r-l;
	for(R i=l;i<r;i++)w[i-l+1]=a[i];
	sort(w+1,w+n+1,cmp);
	for(R i=1;i<=n;i++)s[i]=s[i-1]+f[w[i]];
	for(R i=1;i<=n;i++)
	{
		d[0]=-d[w[i]];
		int p=upper_bound(w+1,w+n+1,0,cmp)-w-1;
		ret[w[i]]+=s[p]*k;
	}
}
void daq(int u)
{
	vs[u]=1;
	ti=d[u]=0;dfsd(u,0);
	calc(in[u],out[u],1);
	for(R i=lst[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(!vs[v])calc(in[v],out[v],-1);
	}
	for(R i=lst[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(!vs[v])daq(grt(v,sz[v]));
	}
}
int main()
{
	mx[0]=inf;int n;
	scanf("%d",&n);
	for(R i=1;i<=n;i++)f[i]=2,g[i]=inf;
	for(R i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		link(u,v);link(v,u);
		--f[u];--f[v];
	}
	for(R i=1;i<=n;i++)if(f[i]==1)q[tl++]=i,g[i]=0;
	bfs();
	daq(grt(1,n));
	for(R i=1;i<=n;i++)printf("%d\n",f[i]==1?1:ret[i]);
	return 0;
}
posted @ 2021-09-22 15:43  nkxjlym  阅读(33)  评论(0)    收藏  举报