zoukankan      html  css  js  c++  java
  • 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。

    ( herefore) 应该选择(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(nlog n))

    Memory complexity: (O(nlog 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('
    ')
    #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);
    }
    
  • 相关阅读:
    js截取字符串区分汉字字母代码
    List 去处自定义重复对象方法
    63. Unique Paths II
    62. Unique Paths
    388. Longest Absolute File Path
    41. First Missing Positive
    140. Word Break II
    139. Word Break
    239. Sliding Window Maximum
    5. Longest Palindromic Substring
  • 原文地址:https://www.cnblogs.com/BrianPeng/p/12340976.html
Copyright © 2011-2022 走看看