标签:否则 节点 贪心 for 开始游戏 codeforce 一个 back 实现
大意: 给定n结点树, 有k桶水, p块钱, 初始可以任选不超过k个点(不能选根结点), 在每个点放一桶水, 然后开始游戏. 游戏每一轮开始时, 可以任选若干个节点关闭, 花费为关闭结点储存水的数量和, 然后未关闭的非根结点上的水会流入父结点, 然后再开始新的一轮. 当所有非根结点无水后游戏结束, 假设第$i$轮流入根节点的水为$w_i$, 游戏共进行了$l$轮, 求$max(w_1,w_2,...,w_l)$
可以发现最优时一定是一段深度相邻的水, 所以双指针维护一段连续的区间就行了.
考虑每个区间的花费怎样计算, 一个显然的贪心策略是: 每次都关闭深度最低的点, 直到这段区间的水深度相同为止.
假设现在维护的区间为$[l,r]$, 若添加一个点$r+1$, 若$dep[r+1]=dep[r]$则花费不增加, 否则需要多等待它一回合, 花费增加$r-l+1$. 若删除左端点$l$, 花费减少量即为左端点的关闭次数$dep[r]-dep[l]$
双指针具体实现的话, 因为有0贡献点的存在, 按我以前的写法会少更新ans, 解决方法是在移动右端点前更新一下ans
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#define pb push_back
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int N = 1e5+10, INF = 0x3f3f3f3f;
int n, k, p, cnt;
vector<int> g[N];
int dep[N];
void dfs(int x, int fa, int d) {
if (x!=1) dep[++cnt] = d;
for (int y:g[x]) if (y!=fa) dfs(y,x,d+1);
}
int main() {
scanf("%d%d%d", &n, &k, &p);
REP(i,2,n) {
int u, v;
scanf("%d%d", &u, &v);
g[u].pb(v),g[v].pb(u);
}
dfs(1,0,0);
sort(dep+1,dep+1+cnt);
int now = 1, w = 0, ans = 0;
REP(i,1,cnt) {
while (w<=p&&now<=cnt) {
ans = max(ans, now-i);
if (dep[now]!=dep[now-1]) w += now-i;
++now;
}
if (w<=p) ans = max(ans, now-i);
w -= dep[now-1]-dep[i];
}
printf("%d\n", min(ans, k));
}
Mashmokh and ACM CodeForces - 414D (贪心)
标签:否则 节点 贪心 for 开始游戏 codeforce 一个 back 实现
原文地址:https://www.cnblogs.com/uid001/p/10425757.html