zoukankan      html  css  js  c++  java
  • 洛谷P5021 赛道修建

    题目

    首先考虑二分,然后发现最小长度越大的话,赛道就越少。所以可以用最终的赛道个数来判断长度是否合理。问题转化为给定一个长度,问最多有多少条互不重叠路径比这个给定长度大。

    考虑贪心,毕竟贪心也是二分check函数的常用做法。原图毕竟为一棵树,每条路径都由一个端点一个终点和他们的(LCA)之间的连边组成。我们直接枚举lca,然后枚举lca的子链且该链上无被找到的链,对于没有找到的链,都匹配上子链上最小的、浪费最少的,考虑这样做为什么是对的,因为如果不练子链上,而去连父亲的话,最底下的链肯定就浪费了。所以可以贪心直接用递归子树,然后找到每个子树中的选择的链,不选择的链。就行了,匹配操作可以用二分优化。

    #include <bits/stdc++.h>
    #define N 1000101
    using namespace std;
    struct edg {
    	int to, nex, len;
    }e[N];
    int n, m, cnt, root = 1, ha, lin[N], dp[N], temp[N], vis[N], tot;//dp[i]表示i的点权
    inline void add(int f, int t, int l)
    {
    	e[++cnt].len = l;
    	e[cnt].to = t;
    	e[cnt].nex = lin[f];
    	lin[f] = cnt;
    }
    void dfs(int now, int fa, int len)
    {
    
    	int tot = 0;
    	for (int i = lin[now]; i; i = e[i].nex)
    	{
    		int to = e[i].to;
    		if (to != fa)
    			dfs(to, now, len);
    	}
    	for (int i = lin[now]; i; i = e[i].nex)
    	{
    		int to = e[i].to;
    		if (to != fa)
    			temp[++tot] = dp[to] + e[i].len;
    	}
    	
    	sort(temp + 1, temp + 1 + tot); //将当前i到结尾的所有链处理出来。
    //	oprintf("%d %d
    ", now, temp[tot]);
    	for (int i = tot; i >= 1 && temp[i] >= len; i--)
    			tot--, ha--;
    	for (int i = 1; i <= tot; i++)
    	{
    		if (vis[i] == now) 
    			continue;//vis判断是否链经过now 
    		int a = len - temp[i];//开始二分 
    		int l = i + 1, r = tot, mid, ans = tot + 1; 
    		while (l <= r)
    		{
    			mid = (l + r) >> 1;
    			if (temp[mid] >= a)//如果当前的另一条链比a大的话,说明此时可以用来求解 
    			{
    				ans = mid;
    				r = mid - 1;
    			}
    			else l = mid + 1; 
    		}
    		while (vis[ans] == now && ans <= tot) 
    			ans++;
    		if (ans <= tot) 
    		{
    			ha--, vis[i] = vis[ans] = now;
    		}
    	}
    	dp[now] = 0;
    	for (int i = tot; i >= 1; i--)//找到第一个最长的链,然后 
    		if (vis[i] != now)
    		{
    			dp[now] = temp[i];
    			break;
    		}
    //	printf("%d
    ", dp[now]);
    }
    bool check(int mid)//判断mid,对于每条路径取到就停止,最后判断是否超过m个比mid大的路径 
    {
    	ha = m;       // now是当前能满足多少个路径。
    	memset(vis, 0, sizeof(vis));
    	dfs(root, 0, mid);
    	return ha <= 0;
    }
    int main()
    {
    	scanf("%d%d", &n, &m);
    	int l = 0, r = 0;
    	for (int i = 1; i < n; i++)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c);
    		add(b, a, c);
    		r += c;
    	}
    	int mid, ans;
    	while (l <= r)
    	{
    		mid = (l + r) >> 1;
    		if (check(mid))
    		{
    			ans = mid;
    			l = mid + 1;
    		}
    		else
    			r = mid - 1;
    	}
    	printf("%d", ans);
    	return 0;
    }
     
    
  • 相关阅读:
    在vs2008中集成JavaScript Lint检查JavaScript语法
    (转载)SQL分页语句
    设置出错页
    判断2个输入框至少输入1个
    C#获取用户桌面等特殊系统路径
    创建存储过程的代码
    SqlParameter关于Like的传参数无效问题
    (转)利用Office里面的OWC组件进行画图
    firefox3不能获得html file的全路径的问题
    (转)使用ASP.NET上传图片汇总
  • 原文地址:https://www.cnblogs.com/liuwenyao/p/11827318.html
Copyright © 2011-2022 走看看