疫情控制

疫情控制

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

\(n,m\le3\times1e5\) 路径长度\(\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\n", 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\n", 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\n", 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\n", totg);
   // for(int i = 1; i <= totg; i++) printf("i = %d %d %d\n", 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\n", 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\n", ans);
    return 0;
}
posted @ 2019-10-15 13:39  Akaina  阅读(227)  评论(0编辑  收藏  举报