zoukankan      html  css  js  c++  java
  • 洛谷P1084 疫情控制

    题目

    细节比较多的二分+跟LCA倍增差不多的思想

    首先有这样一个贪心思路,深度越低的检查点越好,而最长时间和深度具有单调性,即给定时间越长,每个军队能向更浅的地方放置检查点。因此可以考虑二分时间,然后判断军队是否可以放置在控制疫情的地方。

    但是有的军队需要先满足自己当前所在的节点,然后此节点如果有多个军队,其他军队跳到1节点,再跳到1节点的其他子树,这里又有一个贪心策略,就是每个军队跳到1的剩余时间:二分的当前时间减去到1节点的距离越大,就要跳到1的另一个子树里据1最远的点,这样才能更好地使得控制疫情。

    #include <bits/stdc++.h>
    #define N 1001001
    using namespace std;
    struct edg {
     	int to, nex, len;
    }e[N];
    struct tem {
    	int id, len;
    }a[N], b[N]; 
    int n, m, cnt, lin[N], vis[N], vis2[N], army[N], dep[N], fa[N][19], dis[N][19];//fa[i][j]表示军队向上跳j次所到达的位置,dis表示此时所用的时间。 
    //vis表示该点包括的叶子节点是否被完全覆盖 
    bool cmp(tem x, tem y)
    {
    	return x.len < y.len;
    }
    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 f)
    {
     	fa[now][0] = f;
     	dep[now] = dep[f] + 1;
     	for (int i = lin[now]; i; i = e[i].nex)
     	{
     		int to = e[i].to;
     		if (to == f) continue;
     		dis[to][0] = e[i].len;
     		dfs(to, now);
     	}
    }
    void bF(int now)//判断该点的所有叶子节点是否全都被覆盖
    {
    	int b1 = 1, b2  = 0;
    	if (vis[now]) return;
    	for (int i = lin[now]; i; i = e[i].nex)
    	{
    		int to = e[i].to;
    		if (to == fa[now][0]) 
    			continue;
    		b2 = 1;
    		bF(to);
    		if (!vis[to])
    			b1 = 0;
    	}
    	if (b1 && b2 && now != 1)
    		vis[now] = 1;
    }
    bool check(int mid)//使每个军队都尽可能的往上跳,直到距离比mid大才停止。
    {
    	memset(vis, 0, sizeof(vis));
    	memset(vis2, 0, sizeof(vis2));
    	int tnt1 = 0, tnt2 = 0;
     	for (int i = 1; i <= m; i++)
     	{
    	 	int u = army[i], dn = 0;
    	 	for (int j = 18; j >= 0; j--)	 		if (dn + dis[u][j] <= mid && fa[u][j] != 0)	 			 dn += dis[u][j], u = fa[u][j];
    	 	if (u != 1)//如果此点不为1说明此点可以被覆盖。
    	 		vis[u] = 1;
    	 	else
    	 	{
    	 		a[++tnt1].len = mid - dn;
    	 		int u2 = army[i];
    	 		for (int j = 18; j >= 0; j--)	 			if (fa[u2][j] > 1)	 				u2 = fa[u2][j];
    	 		a[tnt1].id = u2;
    		}
     	}
     	bF(1);
    	for (int i = lin[1]; i; i = e[i].nex)//枚举深度为2的点
    	{
    		int to = e[i].to;
    		if (vis[to]) continue;//找到没有完全覆盖完的to; 然后加以覆盖。 选一定要选深度最浅的,所以选择深度为2的,这样选择一定是最优解。
    		b[++tnt2].id = to, b[tnt2].len = e[i].len;
        }
        sort(a + 1, a + 1 + tnt1, cmp);//a的len是剩余的时间
    	sort(b + 1, b + 1 + tnt2, cmp);//b的len是需要的时间, 他们的时间相加
    	int j = 1;
    	for (int i = 1; i <= tnt1; i++) 
    	{
    		if (!vis[a[i].id]) vis[a[i].id] = 1;//先把此点给覆盖了。
    		else if (a[i].len >= b[j].len) vis[b[j].id] = 1; 
    		while (vis[b[j].id] && j <= tnt2) j++;
    	}
    	if (j > tnt2) 
     	return 1;
     	else 
    	return 0;
    }
    inline void init()
    {
     	scanf("%d", &n);
     	for (int i = 1, u, v, w; i < n; i++)
     	{
     		scanf("%d%d%d", &u, &v, &w);
     		add(u, v, w);
     		add(v, u, w);
     	}
     	scanf("%d", &m);
     	for (int i = 1; i <= m; i++)
     		scanf("%d", &army[i]);
     	dfs(1, 0);
     	for (int j = 1; j <= 18; j++)
     		for (int i = 1; i <= n; i++)
     		{
     			fa[i][j] = fa[fa[i][j - 1]][j - 1];
     			dis[i][j] = dis[i][j - 1] + dis[fa[i][j - 1]][j - 1];//dis[i][j]是i向上跳j步所走的路径长度,此叶子节点的意思是最深的节点 
     		}
    }
    int main()
    {
    	init();
    	int ans, l = 0, r = 70000000;
    	while (l <= r)
    	{
    		int mid = (l + r) >> 1;
    		if (check(mid))
    			ans = mid, r = mid - 1;
    		else
    			l = mid + 1;
    	}
    	printf("%d", ans);
    	return 0;
    }
    
  • 相关阅读:
    asp.net获取站点根目录下子目录的名称
    asp.net在网站根目录下创建文件夹
    HoverTree.Model.ArticleSelect类的作用
    HoverTree系统源码介绍
    ASP.NET中Request.RawUrl、Request.Url的区别
    汇编语言-环境搭建
    IDEA快速返回上次代码的位置
    一级建造师
    4000块DIY组装电脑
    职称申报
  • 原文地址:https://www.cnblogs.com/liuwenyao/p/11795070.html
Copyright © 2011-2022 走看看