Solution: 题解 CF1059E Split the Tree

给出一个堆贪心解法

记点\(u\)的深度为\(d_u(d_1=0)\),父亲为\(f_u\),拥有儿子数量\(es_u\)

首先找到每个点的最远延伸点(点\(u\)的最远延伸点记为\(v_u\)),借助树上倍增即可。

接下来是贪心方法

在每次链连接完后删掉这些点,那么每条链的尾端一定是一个叶子。

那么就想办法找出目前贪心最优的叶子,然后往上连接。

以下的贪心本蒟蒻并没有想出严格证明方法,只能感性理解一下了QAQ。

贪心 1:目前\(d_{v_u}\)最小的叶子\(u\)是最优叶子

举个栗子:\(n=5,f_{2..n}=[1,1,2,2],v_{1..n}=[1,1,3,1,2]\)(请自行脑补这棵树)

现在的叶子有\(3,4,5\)

因为\(3\)与其他叶子不冲突,所以直接考虑\(4,5\)即可。

如果选择\(4\),那么最多可以延伸出\(\{1,2,4\}\),剩下\(\{3\},\{5\}\),一共\(3\)条。

如果选择\(5\),那么最多只能延伸出\(\{2,5\}\),剩下的\(3,4\)不能帮忙带走\(1\),所以就\(4\)条了QAQ。

\(\therefore\) 应该选择\(d_{v_u}\)最小的叶子\(u\)以求带走更多的点。

贪心 2:对于最优叶子\(u\),能往上连就往上连

因为是目前最优的叶子,没有其他叶子可以到达\(v_u\),相比于让\(v_u\)重新开一条链,还是用\(u\)带走比较合算。

具体实现方法

构建一个存储点\(u\),以\(d_{v_u}\)为关键字的小根堆。

使用 dfs 求出\(v_u,es_u\),然后把叶子丢进堆里。

每次弹出一个点\(u\),答案计数\(+1\),并且往上连接,将路过的点用\(vs_u\)标记掉。

直到到达\(v_u\)或者该点已被标记,停止连接。

Particularly, 如果到达了\(v_u\)且没有被标记,那么\(f_{v_u}\)就失去了一个儿子,所以要处理\(es_{f_{v_u}}\)并判断入堆。

Time complexity: \(O(n\log n)\)

Memory complexity: \(O(n\log n)\)

具体见代码(\(823\)ms / \(49.40\)MB)

//This program is written by Brian Peng.
#pragma GCC optimize("Ofast","inline","no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define Rd(a) (a=read())
#define Gc(a) (a=getchar())
#define Pc(a) putchar(a)
int read(){
    int x;char c(getchar());bool k;
    while(!isdigit(c)&&c^'-')if(Gc(c)==EOF)exit(0);
    if(c^'-')k=1,x=c&15;else k=x=0;
    while(isdigit(Gc(c)))x=(x<<1)+(x<<3)+(c&15);
    return k?x:-x;
}
void wr(int a){
    if(a<0)Pc('-'),a=-a;
    if(a<=9)Pc(a|'0');
    else wr(a/10),Pc((a%10)|'0');
}
signed const INF(0x3f3f3f3f),NINF(0xc3c3c3c3);
long long const LINF(0x3f3f3f3f3f3f3f3fLL),LNINF(0xc3c3c3c3c3c3c3c3LL);
#define Ps Pc(' ')
#define Pe Pc('\n')
#define Frn0(i,a,b) for(int i(a);i<(b);++i)
#define Frn1(i,a,b) for(int i(a);i<=(b);++i)
#define Frn_(i,a,b) for(int i(a);i>=(b);--i)
#define Mst(a,b) memset(a,b,sizeof(a))
#define File(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define N (100010)
int n,l,s,w[N],f[N][20],g[N][20],ans,d[N],es[N],lg[N]{-1},u,v[N];
bool vs[N];
vector<int>e[N];
struct Cmp{bool operator()(int a,int b)const{return d[v[a]]>d[v[b]];}};
priority_queue<int,vector<int>,Cmp>q;
void dfs(int u);
signed main(){
    Rd(n),Rd(l),Rd(s);
    Frn1(i,1,n)if(Rd(w[i])>s)wr(-1),exit(0);
    Frn1(i,2,n)e[Rd(*f[i])].push_back(i),lg[i]=lg[i>>1]+1;
    dfs(1);
    while(!q.empty()){
        u=q.top(),q.pop(),++ans;
        for(int p(u);!vs[p];p=*f[p]){
            vs[p]=1;
            if(p==v[u]){if(!--es[*f[v[u]]])q.push(*f[v[u]]);break;}
        }
    }
    wr(ans),exit(0);
}
void dfs(int u){
    Frn1(i,1,lg[d[u]])f[u][i]=f[f[u][i-1]][i-1],
        g[u][i]=g[u][i-1]+g[f[u][i-1]][i-1];
    int rs(s-w[v[u]=u]);
    Frn_(i,lg[d[u]],0)if(d[u]-d[v[u]]+(1<<i)<l&&g[v[u]][i]<=rs)
        rs-=g[v[u]][i],i=min(i,d[v[u]=f[v[u]][i]]);
    for(int i:e[u])*f[i]=u,*g[i]=w[u],d[i]=d[u]+1,dfs(i);
    if(!(es[u]=e[u].size()))q.push(u);
}

猜你喜欢

转载自www.cnblogs.com/BrianPeng/p/12340976.html