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;
}
$$Life \quad is \quad fantastic!$$