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

    疫情控制

    题意:有一颗(n)个点的树,有(m)支军队,要使得每个叶子结点到根的路径上都至少存在一支军队,军队移动的时间等于路的长度,求最少的移动时间。

    (n,mle3 imes1e5) 路径长度(le1e9)

    做法:

    二分 + 树上贪心

    (1.)显然二分

    (2.)考虑每支军队,如果不能移动到根节点,则让它待在最高的节点,如果能,考虑把它分配到根节点的其它儿子节点,如果一支军队从根节点的儿子节点到根节点的距离为(x),如果它到根节点后剩余的移动空间小于(x),则它不如待在x,因为如果不这样,就需要拿一个剩余距离大于(x)的军队移动到这个根节点的儿子节点。所以如果一个根的儿子节点存在可以到根但回不来的节点,则在这些节点中选一个到根节点后剩余距离最小的点,把它留在这个节点,再其它点分配到根节点去。然后对到根节点的点排个序,贪心分配一下,(再dfs一遍check一下就可以了)。

    这道题思路只要想到了一些情况的处理方法,还是比较容易理解的,但是代码实现的时候,可能会遇到很多问题,要开各种数组,对代码实现能力的考核较大。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define maxn 50100
    #define ll long long
    int n, m;
    ll l = 0, r = 0;
    int fir[maxn], nxt[maxn * 2], vv[maxn * 2];
    ll edg[maxn * 2];
    int tot = 0;
    void add(int u, int v, int w)
    {
        nxt[++tot] = fir[u];
        fir[u] = tot;
        vv[tot] = v;
        edg[tot] = w;
    }
    int f[maxn][22];
    ll dis[maxn][22];
    int dep[maxn], s[maxn];
    void Deal_first(int u, int fa)
    {
        dep[u] = dep[fa] + 1;
        for(int i = 0; i <= 19; i++)
        {
            dis[u][i + 1] = dis[u][i] + dis[f[u][i]][i];
            f[u][i + 1] = f[f[u][i]][i];
        }
        for(int i = fir[u]; i; i = nxt[i])
        {
            int v = vv[i];
            if(v == fa) continue;
      //      printf("v = %d u = %d edg = %d
    ", v, u, edg[i]);
            f[v][0] = u;
            dis[v][0] = edg[i];
            Deal_first(v, u);
        }
    }
    bool cmp(pair<ll ,int> x, pair<ll, int> y)
    {
        return x.first < y.first;
    }
    pair <ll, int> g[maxn];
    ll p[maxn];
    int pd[maxn], cnt[maxn];
    int totg = 0, totp = 0, totf1 = 0;
    ll f1[maxn];
    bool cmp1(int x, int y)
    {
        return dis[x][0] < dis[y][0];
    }
    bool cmp2(int x, int y)
    {
        return x < y;
    }
    int dfs(int u, int fa)
    {
        if(pd[u] == 1) return 1;
        int tmp = 0;
        for(int i = fir[u]; i; i = nxt[i])
        {
            int v = vv[i];
            if(v == fa) continue;
            tmp = 1;
            if(!dfs(v, u)) return 0;
        }
        if(!tmp) return 0;
        return 1;
    }
    int check(ll lim)
    {
        memset(g, 0, sizeof(g));
        memset(p, 0, sizeof(p));
        memset(cnt, 0, sizeof(cnt));
        memset(pd, 0, sizeof(pd));
        memset(f1, 0, sizeof(f1));
        totg = 0; totf1 = 0; totp = 0;
       // printf("lim = %lld
    ", lim);
        for(int i = 1; i <= m; i++)
        {
            int u = s[i];
            ll cst = 0;
            for(int j = 20; j >=0 ; j--)
            {
                if(f[u][j] == 0) continue;
            //    printf("f[%d][%d] = %d dis[][] = %d
    ", u, j, f[u][j], dis[u][j]);
                if(f[u][j] != 1 && cst + dis[u][j] <= lim)
                {
                    cst += dis[u][j]; u = f[u][j];
                }
            }
            if(f[u][0] == 1 && cst + dis[u][0] <= lim)
            {
                g[++totg] = make_pair(lim - cst, u);
            }
            else pd[u] = 1;
        }
       // printf("totg = %d
    ", totg);
       // for(int i = 1; i <= totg; i++) printf("i = %d %d %d
    ", i, g[i].first, g[i].second);
        for(int i = fir[1]; i; i = nxt[i])
        {
            int v = vv[i];
            if(!dfs(v, 1)) cnt[v] = 1;
        }
        sort(g + 1, g + totg + 1);
        for(int i = 1; i <= totg; i++)
        {
            if(cnt[g[i].second] == 1 && g[i].first < dis[g[i].second][0] * 2)
            {
                cnt[g[i].second] = 0;
                continue;
            }
            p[++totp] = g[i].first - dis[g[i].second][0];
        }
        for(int i = fir[1]; i; i = nxt[i])
        {
            int v = vv[i];
            if(cnt[v] == 1)
            {
                f1[++totf1] = dis[v][0];
            }
        }
        sort(f1 + 1, f1 + totf1 + 1);
        sort(p + 1, p + totp + 1);
        int l = 1, r = 1;
        while(l <= totf1 && r <= totp)
        {
            if(f1[l] <= p[r])
            {
                l ++; r ++;
            }
            else r ++;
        }
        if(l == totf1 + 1) return 1;
        return 0;
    }
    int main()
    {
        scanf("%d", &n);
        for(int i = 1; i < n; i++)
        {
            int u, v; ll w; scanf("%d%d%lld", &u, &v, &w);
            add(u, v, w); add(v, u, w);
            r += w;
        }
        Deal_first(1, 1);
      //  for(int i = 1; i <= n; i++) printf("dis[%d][0] = %d
    ", i, dis[i][0]);
        scanf("%d", &m);
        for(int i = 1; i <= m; i++) scanf("%d", &s[i]);
        ll ans = -1;
        while(l <= r)
        {
            ll mid = (l + r) >> 1;
            if(check(mid) == 1)
            {
                ans = mid;
                r = mid - 1;
            }
            else l = mid + 1;
        }
        printf("%lld
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    .NET 正则表达式使用高级技巧之替换类介绍
    道法术器势
    JS函数匿名替换
    批量更改数据库表架构(生成sql后直接执行!)
    转: 从现实生活中理解什么是广播机制
    public View getView(int position, View convertView, final ViewGroup parent)三个参数的意思
    Android Intent个人介绍
    WPF中Timer与DispatcherTimer类的区别
    C# 使用ManualResetEvent 进行线程同步
    C# 使用AutoResetEvent进行线程同步
  • 原文地址:https://www.cnblogs.com/Akaina/p/11677162.html
Copyright © 2011-2022 走看看