zoukankan      html  css  js  c++  java
  • 题解 P5021【NOIP2018】 【赛道修建】

    (LARGE exttt{P5021})

    做法:贪心/二分/( exttt{DFS})(树形 ( exttt{DP}))/并查集

    前言

    清一色的 ( exttt{multiset}) ,为什么啊?

    这的确是一道好题,做法很多,对于这里贪心的维护难道非要经行删除操作?(还有为什么有人说显然要 ( exttt{multiset}) ?)像我这样的蒟蒻不会 ( exttt{multiset}) 怎么办?

    提供一种不用 ( exttt{multiset}) 的做法(码量小)。

    题意

    给你一颗带权树,要你选 (k) 条没有重边的链,使得这些链中权值最小值最大。

    这里链的权值定义为链上所有边权之和。

    思路

    好像做过一道类似的题

    ( exttt{undate}):确实为( exttt{P6147})的加强版。

    看到最小值最大,显然二分答案,且有十分明显的单调性,然后 (check) 一下就好了。

    (check) 里面是一个经典的树形 ( exttt{DP}),考虑从叶子节点往上处理,因为对于每个节点向上到父亲的边只有一条,所以对于每个节点记录从这个节点一直往下最深并没有选进链的链(以下记为 (s[i]) ,如何得到这个值请往下看)。

    对于每一个节点,我们要进行两种操作。

    • 若以这个节点为链的端点,有大于目标值的链,直接对答案贡献 (+1)

    • 若以这个节点为某一条链,两个端点的 ( exttt{LCA}) ,则要做一个拼接操作,将两个儿子的 (s[i]+dis(fa,son_i)) 值合并,但这两个 (s) 值相加必须要大于目标值。

      剩下的儿子节点中最大的 (s[i]+dis(fa,son_i)) 作为 (s[fa])

    可以证明,先要优先最大化合并操作的次数,因为若要优先最大化 (s[i]) ,则可能这第 (i) 个节点的合并个数会变小,但 (s[i]) 增大最多对i的父节点的贡献 (+1) 。(贪心*1)

    但是在优先最大化每个节点合并的次数,还要次优先最大化 (s[i]) ,原本我有一个错误的贪心:是先排序 (s[son\_of\_i]) ,然后不断将最左边的数和最右边的数合并,若不能合并,则将最左边的值删去。

    但是这个贪心是错的,参考样例:2 8 9 目标值:10 wrong:s[n]=8 ans:s[n]=9

    发现这里疏忽了一点,就是应该选择这个数最小能匹配的数,可以用 ( exttt{lower\_bound}) 求出。(贪心*2)

    求出那个数之后,要将它删去,这里我用 (Large exttt{并查集}) 来维护,若第 (i) 个数被删去,就将自己的父亲定为 (i+1) 。然后取二分到的那个数的祖宗,若这个祖宗是 (s.size()+1) (即这个数后面的数都被删完了),就跳过这个数。

    时间复杂度 (O((logsum val) imes Nlog N imesalpha(N))) (远远达不到的,本人不会均摊QwQ

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
     #define PB push_back
     #define MP make_pair
     const int N = 5e4;
     #define pii pair<int,int>
    inline int read()
    {
        int s = 0;
        register bool neg = 0;
        register char c = getchar();
        for (; c < '0' || c > '9'; c = getchar())
            neg |= (c == '-');
        for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
            ;
        s = (neg ? -s : s);
        return s;
    }
    
    int a,b,p,res,s[N+10],nxt[N+10],lst[N+10],Fa[N+10];
    bool vis[N+10];
    vector<pair<int,int> >st[N+10];
    
    inline int f(int n) {
    	return Fa[n]==n?n:Fa[n]=f(Fa[n]);
    }
    
    inline void dfs(int n,int fa) {
    	s[n]=0;
    	vector<int> q;
    	q.clear();
    	for(int i=0;i<st[n].size();i++) {
    		int v=st[n][i].first,val=st[n][i].second;
    		if(v==fa) continue;
    		dfs(v,n);
    		if(s[v]+val>=p) res++;
    		else q.PB(val+s[v]);
    	}
    	if(!q.size()) return;
    	if(q.size()==1) {
    		s[n]=q[0];
    		return;
    	}
    	sort(q.begin(),q.end());
    	for(int i=0;i<=(int)q.size();i++) Fa[i]=i,vis[i]=0;
    	for(int i=0;i<(int)(q.size()-1);i++) {
    		if(vis[i]) continue;
    		if(i>=q.size()-1||i==-1) break;
    		int t=lower_bound(q.begin()+i+1,q.end(),p-q[i])-q.begin();
    		if(t>=q.size()) continue;
    		t=f(t);
    		if(t>=q.size()) continue;
    		if(q[t]+q[i]<p) continue;
    		vis[t]=1;
    		vis[i]=1;
    		Fa[t]=t+1;
    		res++;
    	}
    	for(int i=(int)(q.size()-1);i>=0;i--) if(!vis[i]) {
    		s[n]=q[i];
    		break;
    	}
    }
    
    inline bool check(int n) {
    	p=n;
    	res=0;
    	dfs(1,0);
    	return res>=b;
    }
    
    signed main()
    {
    //	freopen("in.txt","r",stdin);
    	a=read();
    	b=read();
    	int x,y,z,mx=0;
    	for(int i=1;i<a;i++) {
    		x=read();
    		y=read();
    		z=read();
    		mx+=z;
    		st[x].PB(MP(y,z));
    		st[y].PB(MP(x,z));
    	}
    	int l=1,r=mx,ans=0;
    	while(l<=r) {
    		int mid=(l+r)>>1;
    		if(check(mid)) l=mid+1,ans=mid;
    		else r=mid-1;
    	}
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    Kubernetes1.91(K8s)安装部署过程(一)--证书安装
    开源仓库Harbor搭建及配置过程
    有关centos7 图形化root用户登录
    linux服务器查看tcp链接shell
    django表格form无法保存评论排查步骤
    Redis 4.x 安装及 发布/订阅实践和数据持久化设置
    django博客项目-设置django为中文语言
    windows 环境下如何使用virtualenv python环境管理工具
    【转载】python中利用smtplib发送邮件的3中方式 普通/ssl/tls
    php安装phpize工具
  • 原文地址:https://www.cnblogs.com/RedreamMer/p/14084275.html
Copyright © 2011-2022 走看看