P3942 将军令

题目

P3942 将军令

给定\(n\)个节点的树,节点上可以放小队,每个小队可以控制距离该点不超过\(k\)的所有点,问最少放多少小队能全部控制\(n\)个节点

\(n\le10^5,k\le 20\)

思路

  • \(5pts\)

直接输出\(n\)

  • \(45pts,k=1\)

P2279 [HNOI2003]消防局的设立很类似,设\(f[i][0/1/2]\)表示\(i\)这个点被儿子\(0\),自己\(1\),被父亲\(2\)控制

$f[u][0]=sum( \min (f[v][0],f[v][1]))- \min(0,\max(f[v][1]-f[v][0]) ) $

\(f[u][1]=sum(\min(f[v]))\)

\(f[u][2]=sum(\min(f[v][0],f[v][1]))\)

  • \(75pts,k=2\)

和上面的一样,只要在第二维再加两个状态,孙子和儿子全部控制但自己没被控制\(3\)和孙子全被控制但是自己和儿子不全被控制\(4\)

  • \(90pts\)

依旧再加状态,不在赘述

  • \(100pts\)

如果崩了,请联系我

  • 贪心\(100pts\)

这种题目有个结论,一定是在该点的\(k\)级祖先(若没有则是根节点)

那么从下往上更新答案即可

code

/*
@ author:pyyyyyy/guhl37
-----思路------

-----debug-------

*/
#include<bits/stdc++.h>
#include<queue>
using namespace std;
const int N=1e5+2020;
struct node
{
	int v,Next;
}e[N<<1];
int head[N],cnt;
int n,k,t;
void add(int u,int v)
{
	e[++cnt].Next=head[u];
	head[u]=cnt;
	e[cnt].v=v;
}
int dep[N],fa[N],vis[N];
int find(int u)
{
	int step=1;
	while(step<=k)
	{
		step++;
		u=fa[u];
	}
	return u;
}
void dfs1(int u)
{
	for(int i=head[u];i;i=e[i].Next)
	{
		int to=e[i].v;
		if(to==fa[u]) continue;
		fa[to]=u;dep[to]=dep[u]+1;
		dfs1(to);
	}
}
int ans=0;
priority_queue< pair<int,int> > q;
void update(int u,int calmfa,int step)
{
	vis[u]=1;
	if(step==k) return ;
	for(int i=head[u];i;i=e[i].Next)
	{
		int to=e[i].v;
		if(to==calmfa) continue;
		update(to,u,step+1);
	}
}
int main() {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int x,y,u,v;
	scanf("%d%d%d",&n,&k,&t);
	for(int i=1; i<n; i++) {
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dep[1]=1;fa[1]=1;
	dfs1(1);
	for(int i=1;i<=n;++i) q.push(make_pair(dep[i],i));
	while(!q.empty())
	{
		int u=q.top().second;q.pop();
		if(vis[u]) continue;
		ans++;
		int kfa=find(u);
		update(kfa,kfa,0);
	}
	cout<<ans;
	return 0;
}
posted @ 2020-06-18 17:53  pyyyyyy  阅读(133)  评论(0编辑  收藏  举报