题解 P4107 [HEOI2015]兔子与樱花
Posted colazcy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P4107 [HEOI2015]兔子与樱花相关的知识,希望对你有一定的参考价值。
思路好想,卡常不好卡
Solution [HEOI2015]兔子与樱花
题目大意:给定一个树,每个节点有一个权值。如果删除一个节点的话,就将它的权值加到它父节点上,并将它的儿子接到父节点上。要求在任意时刻每个节点的权值与儿子个数和小于常数(m),求最多可以删去多少个节点
贪心
分析:
比较显然的做法是,我们考虑一个节点的”危害":即删去它后,父节点的权值与儿子个数和会增加多少
危害值:(w[x]=c[x]+son[x]-1)
那么我们把危害值从小到大排序,贪心能删就删即可
删除一个节点同时会影响它父亲的危害值,所以你大可以手打斐波那契堆,事实上有一个更好的做法,我们优先删除深度大的节点。也就是按深度一层层贪心。有几点性质保证正确性
- 1.如果先删除父节点再删除子节点合法,那么反过来一定合法
证明:设节点(x),父节点(f),祖父节点(ff)
先删除父亲满足
(c[x]+c[f]+c[ff]+son[ff]-1+son[f]-1+son[x] leq m)
先删除子节点需要额外满足
(c[x]+c[f]+son[f]-1+son[x] leq m)
(ecause son[ff] geq 1)
( herefore c[ff]+son[ff]-1 geq 0)
得证
- 2.如果一个节点在第一次处理它时没有被删去,那么它以后也不可能再被删去了
如果将该节点的危害值加给它父亲不合法,那么如果要在以后删除它,哪怕它父亲、它祖父只有(1)个儿子,把它危害值加给它祖父也一定不合法了((c[x]+son[x])单调不降)
所以我们一层层贪心就可以了,删不了的就直接丢掉不管,为了优化可以写成(dfs)的形式。回溯的时候计算就可以了(兄弟节点互不影响)
#include <cstdio>
#include <cctype>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
const int maxn = 2e6 + 100;
namespace FastIO{
const int bufsiz = 1 << 22;
char buf[bufsiz];
inline char getch(){
static int tot;
tot++;
if(tot == bufsiz){
fread(buf,1,bufsiz,stdin);
tot = 0;
}
return buf[tot];
}
inline int read(){
int x = 0;char c = getch();
while(!isdigit(c))c = getch();
while(isdigit(c))x = x * 10 + c - ‘0‘,c = getch();
return x;
}
}using FastIO::read;
vector<int> vec[maxn];
int c[maxn],son[maxn],head[maxn],nxt[maxn],to[maxn],node[maxn],l[maxn],r[maxn],n,m,ans,tot;
inline void addedge(int from,int to){
static int tot;
::to[++tot] = to;
nxt[tot] = head[from];
head[from] = tot;
}
inline void dfs(int u = 1,int faz = -1){
for(int i = head[u];i;i = nxt[i]){
int v = to[i];
if(v == faz)continue;
dfs(v,u);
}
if(!son[u])return;
sort(node + l[u],node + r[u] + 1,[](int a,int b){return (son[a] + c[a]) < (son[b] + c[b]);});
for(int i = l[u];i <= r[u];i++)
if(c[u] + son[u] + c[node[i]] + son[node[i]] - 1 <= m){
ans++;
c[u] += c[node[i]];
son[u] += son[node[i]] - 1;
}
}
int main(){
// freopen("fafa.in","r",stdin);
n = read(),m = read();
for(int i = 1;i <= n;i++)c[i] = read();
for(int u = 1;u <= n;u++){
int k = son[u] = read();
if(!k)continue;
l[u] = tot + 1;
r[u] = tot + k;
while(k--){
int v = node[++tot] = read() + 1;
addedge(u,v);
}
}
dfs();
printf("%d
",ans);
return 0;
}
以上是关于题解 P4107 [HEOI2015]兔子与樱花的主要内容,如果未能解决你的问题,请参考以下文章