标签:print 优化 node 题目 回溯 要求 证明 getc 链接
思路好想,卡常不好卡
题目大意:给定一个树,每个节点有一个权值。如果删除一个节点的话,就将它的权值加到它父节点上,并将它的儿子接到父节点上。要求在任意时刻每个节点的权值与儿子个数和小于常数\(m\),求最多可以删去多少个节点
贪心
分析:
比较显然的做法是,我们考虑一个节点的”危害":即删去它后,父节点的权值与儿子个数和会增加多少
危害值:\(w[x]=c[x]+son[x]-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\)
\(\because son[ff] \geq 1\)
\(\therefore c[ff]+son[ff]-1 \geq 0\)
得证
如果将该节点的危害值加给它父亲不合法,那么如果要在以后删除它,哪怕它父亲、它祖父只有\(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\n",ans);
return 0;
}
标签:print 优化 node 题目 回溯 要求 证明 getc 链接
原文地址:https://www.cnblogs.com/colazcy/p/13369561.html