AT_arc116_e [ARC116E] Spread of Information 题解

题目传送门

前置知识

二分答案 | 树形 DP

解法

答案显然具有单调性,考虑二分答案。

设当前二分出的答案为 \(mid\),则等价于覆盖距离为 \(mid\) 的情况下进行选点。

做法同 luogu P3942 将军令 ,考虑进行贪心,对于深度最深的叶节点将选择的点放在边界时,即取 \(mid\) 级祖先时,覆盖的范围一定最大。

\(f_{x}\) 表示 \(x\) 到以 \(x\) 为根的子树内最远的没被覆盖的点的距离,\(g_{x}\) 表示 \(x\) 到以 \(x\) 为根的子树内最近被选的点的距离,状态转移方程为 \(\begin{cases} f_{x}=\max\limits_{y \in Son(x)}\{ f_{y}+1 \} \\ g_{x}=\min\limits_{y \in Son(x)}\{ g_{y}+1 \}\end{cases}\)

\(g_{x}>mid\) 时以 \(x\) 为根的子树内所选的点覆盖不到自己,需要祖先节点进行覆盖,此时需要统计自己的贡献,即 \(f_{x}=\max(f_{x},0)\);当 \(f_{x}+g_{x} \le mid\) 时以 \(x\) 为根的子树内所选的点就能覆盖整棵子树,令 \(f_{x}=- \infty\);当 \(f_{x}=mid\) 说明 \(x\) 必须被选,令 \(f_{x}=- \infty,g_{x}=0\),选择点数加一。

特判下根节点没有被覆盖的情况。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
struct node
{
    int nxt,to;
}e[400010];
int head[400010],f[400010],g[400010],cnt=0,sum=0;
void add(int u,int v)
{
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
void dfs(int x,int fa,int k)
{
	f[x]=-0x3f3f3f3f;
	g[x]=0x3f3f3f3f;
	for(int i=head[x];i!=0;i=e[i].nxt)
	{
		if(e[i].to!=fa)
		{
			dfs(e[i].to,x,k);
			f[x]=max(f[x],f[e[i].to]+1);
			g[x]=min(g[x],g[e[i].to]+1);
		}
	}
	if(g[x]>k)
	{
		f[x]=max(f[x],0);
	}
	if(f[x]+g[x]<=k)
	{
		f[x]=-0x3f3f3f3f;
	}
	if(f[x]==k)
	{
		f[x]=-0x3f3f3f3f;
		g[x]=0;
		sum++;
	}
}
bool check(int mid,int k)
{
	sum=0;
	dfs(1,0,mid);
	sum+=(f[1]>=0);
	return sum<=k;	
}
int main()
{
	int n,k,u,v,l=0,r,mid,ans=0,i;
	cin>>n>>k;
	r=n;
	for(i=1;i<=n-1;i++)
	{
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	while(l<=r)
	{
		mid=(l+r)/2;
		if(check(mid,k)==true)
		{
			ans=mid;
			r=mid-1;
		}
		else
		{
			l=mid+1;
		}
	}
	cout<<ans<<endl;
    return 0;
}

后记

多倍经验:luogu P3942 将军令 | luogu P2279 [HNOI2003] 消防局的设立 | luogu P2899 [USACO08JAN] Cell Phone Network G | UVA1218 完美的服务 Perfect Service | luogu P3523 [POI2011] DYN-Dynamite

posted @ 2024-07-25 15:14  hzoi_Shadow  阅读(27)  评论(0编辑  收藏  举报
扩大
缩小