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);
    }
    
  • 相关阅读:
    OpenAL播放pcm或wav数据流-windows/ios/android(一)
    Windows录音API学习笔记--转
    Windows基础-实时录音程序(WaveXXX)
    Windows基础-使用XAudio2播放音频(本质是WASAPI)
    XAudio2播放PCM
    jps的用法及常见问题介绍
    eureka添加security验证之后,client注册失败
    Maven中央仓库地址大全,Maven中央仓库配置示例
    Maven入门指南:仓库
    Maven中央仓库地址
  • 原文地址:https://www.cnblogs.com/BrianPeng/p/12340976.html
Copyright © 2011-2022 走看看