洛谷1084 疫情控制

一道贪心+LCA倍增法的预处理+二分答案

原题链接

又是一道细节贼多的题。。敲了我一个晚上。。

显然可以二分答案,然后就是判定的问题。
贪心地发现军队越在靠近首都的点建立检查站,则贡献越大。所以我们让所有军队都往首都靠近,这时所有军队要么无法到达首都的子节点,要么已经到达首都的子节点,而对于前者,我们直接让他们在停下的点建立检查站,然后针对后者考虑。
对于每个首都的子节点,如果该节点的子树中的叶子节点没有全部被检查站控制,且该首都子节点中所有驻扎的军队中剩余时间最少的军队不足以到达首都再回来,那么这个我们就直接让这个军队直接在该点建立检查站。
最后我们将剩下需要建立检查站的首都子节点按其到首都的距离从小到大排序,再将剩余所有军队按剩余时间减去其到首都的距离的最终剩余时间从小到大排序,然后枚举检查是否每个点都能建立检查站即可。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
#include<cstring>
using namespace std;
const int N = 5e4 + 10;
typedef long long ll;
int fi[N], di[N << 1], da[N << 1], ne[N << 1], f[N][16], a[N], de[N], b[N], la[N], rd[N], id[N], fid[N], L[N], l, gn, n, m, rl;
ll D[N][16], dis[N], res[N];
bool v[N], on[N];
vector<ll>army[N];
int re()
{
    int x = 0;
    char c = getchar();
    bool p = 0;
    for (; c<'0' || c>'9'; c = getchar())
        p |= c == '-';
    for (; c >= '0'&&c <= '9'; c = getchar())
        x = x * 10 + (c - '0');
    return p ? -x : x;
}
inline void add(int x, int y, int z)
{
    di[++l] = y;
    da[l] = z;
    ne[l] = fi[x];
    fi[x] = l;
}
void dfs(int x)
{
    int i, y;
    for (i = 1; i <= gn; i++)
    {
        f[x][i] = f[f[x][i - 1]][i - 1];
        D[x][i] = D[x][i - 1] + D[f[x][i - 1]][i - 1];
    }
    for (i = fi[x]; i; i = ne[i])
    {
        y = di[i];
        if (!de[y])
        {
            de[y] = de[x] + 1;
            f[y][0] = x;
            D[y][0] = da[i];
            dis[y] = dis[x] + da[i];
            dfs(y);
        }
    }
}
bool dfs_2(int x)
{
    int i, y;
    bool p = 0;
    v[x] = 1;
    if (!id[x] && on[x])
        return true;
    for (i = fi[x]; i; i = ne[i])
    {
        y = di[i];
        if (!v[y])
        {
            p = 1;
            if (!dfs_2(y))
                return false;
        }
    }
    return p;
}
bool judge(ll mid)
{
    int i, j, po = 0, ar = 0;
    ll s;
    for (i = 1; i <= rl; i++)
        if (L[i])
        {
            army[i].clear();
            L[i] = 0;
        }
    memset(v, 0, sizeof(v));
    memset(on, 0, sizeof(on));
    v[1] = 1;
    for (i = 1; i <= m; i++)
    {
        b[i] = a[i];
        s = mid;
        for (j = gn; ~j; j--)
            if (f[b[i]][j] && (f[b[i]][j] ^ 1) && D[b[i]][j] <= s)
            {
                s -= D[b[i]][j];
                b[i] = f[b[i]][j];
            }
        on[b[i]] = 1;
        j = id[b[i]];
        if (j)
        {
            army[j].push_back(s);
            L[j]++;
            if (L[j] > 1 && army[j][L[j] - 2] < s)
                swap(army[j][L[j] - 2], army[j][L[j] - 1]);
        }
    }
    for (i = 1; i <= rl; i++)
    {
        if (!dfs_2(fid[i]))
            if (L[i] && army[i][L[i] - 1] < (rd[i] << 1))
            {
                army[i].pop_back();
                L[i]--;
            }
            else
                la[++po] = rd[i];
        for (j = 0; j < L[i]; j++)
            if (army[i][j] >= rd[i])
                res[++ar] = army[i][j] - rd[i];
    }
    if (po > ar)
        return false;
    sort(la + 1, la + po + 1);
    sort(res + 1, res + ar + 1);
    for (; po; po--, ar--)
        if (res[ar] < la[po])
            return false;
    return true;
}
int main()
{
    int i, x, y, z;
    ll l = 0, r, mid, s = 0;
    n = re();
    gn = log2(n);
    for (i = 1; i < n; i++)
    {
        x = re();
        y = re();
        z = re();
        add(x, y, z);
        add(y, x, z);
        s += z;
        if (!(x ^ 1) || !(y ^ 1))
        {
            rd[++rl] = z;
            id[x = (x ^ 1) ? x : y] = rl;
            fid[rl] = x;
        }
    }
    m = re();
    for (i = 1; i <= m; i++)
        a[i] = re();
    sort(a + 1, a + m + 1);
    de[1] = 1;
    dfs(1);
    r = s;
    while (l < r)
    {
        mid = (l + r) >> 1;
        if (judge(mid))
            r = mid;
        else
            l = mid + 1;
    }
    printf("%lld", !(l ^ (s + 1)) ? -1 : l);
    return 0;
}

ps:因为我用的是\(VS\),所以代码会自动补空格,而我本来的风格是没有空格的。

猜你喜欢

转载自www.cnblogs.com/Iowa-Battleship/p/9593963.html