大意: 给定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)); }
原文地址:https://www.cnblogs.com/uid001/p/10425757.html
时间: 2024-12-28 13:00:49