洛谷 U141097 喂猫自动机
洛谷 U141097 喂猫自动机
题目背景
众所周知,机房的大哥是只猫……
题目描述
机房的猫猫自从常住在这里之后,引来了很多小猫来吃饭,为了方便地给小猫们投食,我们在树上模拟出了喂猫自动机。
小猫来吃饭的路径是一个树形结构。一共有 kk 只小猫,在小猫移动之前你可以任意安排他们的位置,但是这时不能有两只猫被放在同一个位置上。一共会经过若干刻钟,你一共有 pp 块钱,每刻钟你可以选择某些节点放上栅栏,放栅栏的花费就是在这个点上目前有几只小猫。所有没有被拦住的小猫都会在这一刻钟走到所在点的父亲节点(小猫不会连续移动)。所有被放置的栅栏在下一刻钟就会被收走,也就是说栅栏只能作用于当前这一刻钟。
而每一刻钟结束处于根节点(11 号节点)的所有猫咪都可以吃到好吃的,然后他们就会离开。而在根节点吃到饭的猫咪数量就是这一刻钟的喂猫数。
现在你想知道喂猫数最大的一刻钟,喂猫数可以达到多少。
输入格式
从文件cat_automaton.incat_automaton.i**n中读入数据。
第一行,33 个整数 n, k, pn,k,p,表示喂猫自动机一共有 nn 个节点,kk 只猫,你手中有 pp 块钱。
接下来的 n-1n−1 行,每行两个整数 u_i,v_iu**i,v**i,描述树上的一条边。
输出格式
输出到文件cat_automaton.outcat_automaton.out中。
输出一行一个整数,在喂猫数最大的一刻钟喂猫数可以达到多少。
命题背景:
时间不等人啊,当初还是萌新OI选手,现在已经快要老年退役了。
摘的去年CSP前信心赛的原题。
真是一段难忘的时光。
题解:
这题给了很多自由发挥的空间,比如随便放、随便拦等等。所以考虑贪心而非DP,发现最优的决策一定是越来越多的人都在到达根节点之前到达同一深度,同理,那么两个人一开始被放置的位置深度差越大,把一个人拦下使得另一个人追上他的难度(花费)就越大。所以我们要尽可能地让人们的初始深度差值最小。也就是说,我们要将其放在:按深度排序后深度相邻的点上。
把所有点按深度排序后,假设深度为\(d\)的点有\(cnt[d]\)个放上人,所有放人的节点中深度最大的为\(D\),那么为了让他们达到同一深度,需要的代价就是:
这样,为了控制\(l,r\)选取的范围,我们这里采用双指针法。
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int n,k,p;
int tot,head[maxn],nxt[maxn<<1],to[maxn<<1];
int deep[maxn],cnt[maxn];
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x,int f)
{
deep[x]=deep[f]+1;
cnt[deep[x]]++;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==f)
continue;
dfs(y,x);
}
}
int main()
{
scanf("%d%d%d",&n,&k,&p);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
sort(deep+1,deep+n+1);
int l=2,r=2,ans=1,tmp=0;
while(1)
{
if(l>=n || r>=n)
break;
r++;
if(deep[r]!=deep[r-1])
tmp+=(r-l);
while(tmp>p || r-l+1>k)
{
tmp-=(deep[r]-deep[l]);
l++;
}
ans=max(ans,(r-l+1));
}
printf("%d",ans);
return 0;
}