Luogu1084 疫情控制

https://www.luogu.com.cn/problem/P1084

二分/倍增/贪心

阅读题面,很容易发现,答案具有可以二分的性质,所以我们首先二分答案

又有一个比较显然的结论,我们假设让一支军队在一个节点或其父亲(非\(1\))上,选择其父亲显然更优(可以覆盖更多叶子节点)

那我们就倍增跳呗!

问题是,对于一支跳到最远的祖先后,还有更多步数军队,它还有一个选择,就是穿过\(1\)去访问其他节点

由于我们不容易决策,姑且先把军队都堆积到最远的祖先上(跳不到的,在最远的位置建立检查站即可)

这里有个错误的想法,就是取堆积的点中剩余步数最小的军队,让该军队建立检查站

因为可以存在交叉建检查站更优的情况,因为拥有更多剩余步数的军队可以去更远的未覆盖的点建立检查站

我尝试了一下,居然可以水到\(80pts\)\(loj90pts\)

那怎么办呢?

我们考虑一个更简单的问题,假如把军队全部堆积到\(1\)号节点,怎么做呢?

很显然的贪心,让拥有更多剩余步数的军队去较远的点建立检查站

考虑原问题,我们仍然可以把军队全部堆积到\(1\)号节点

但是这些军队本来还有一个选择,就是驻守在原来的地方,而不跨越\(1\)号节点

也就是说,对于每支军队,它都有权不用任何花费驻守在原来的地方

我们还是先从大到小排序,一个个判断

假设现在是\(a_i\)\(b_i\)匹配(\(a_i\)之后的\(a_j\)都小于\(a_i\)\(b_i\)同理)

\(b_i\)有原本驻守在该地的\(a_k\),我们找到的\(a_k\)应当是权值最小的,由于\(a_k \le a_i\),选择\(a_k\)更优,直接选择\(a_k\)

\(b_i\)无原本驻守在该地的\(a_k\),那么直接比较\(a_i,b_i\),若\(a_i<b_i\),说明匹配失败,当前情况不可行

权值最小的\(a_k\),直接用\(multiset\)维护(注:菜死,每个节点最多覆盖一次,直接记录覆盖一个节点的拥有最小剩余步数的军队就好了)

然后本题就解决了

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#define ll long long
#define N 600005
#define RN 300005
#define pr pair<int,int>
#define mp make_pair
using namespace std;
int n,m,x,y,z,mxz,tot,mn[RN],kz[RN],f[RN][22],fr[RN],nxt[N],d1[N],d2[N];
ll ans,l=0,r=0,len[RN];
bool wro[RN],tag[RN],al[RN];
multiset< pr >s;
multiset< pr >t[RN];
pr xx[RN],q[RN];
void add(int x,int y,int z)
{
    tot++;
    d1[tot]=y;
    d2[tot]=z;
    nxt[tot]=fr[x];
    fr[x]=tot;
}
void dfs(int u)
{
    for (int i=fr[u];i;i=nxt[i])
    {
        int v=d1[i];
        int cost=d2[i];
        if (v==f[u][0])
            continue;
        f[v][0]=u;
        len[v]=len[u]+cost;
        r=max(r,len[v]);
        dfs(v);
    }
}
void judge(int u)
{
    if (tag[u])
        return;
    if (u!=1 && !nxt[fr[u]])
        wro[u]=true;
    for (int i=fr[u];i;i=nxt[i])
    {
        int v=d1[i];
        if (v==f[u][0])
            continue;
        judge(v);
        wro[u]|=wro[v];
        if (u!=1 && wro[u])
            return;
    }
}
bool cmp(pr x,pr y)
{
    return x.first>y.first;
}
bool check(ll mid)
{
    int q0=0,x0=0;
    s.clear();
    for (int i=1;i<=n;i++)
        wro[i]=tag[i]=false,mn[i]=mxz+mxz+1,t[i].clear();
    for (int i=1;i<=m;i++)
    {
        int g=kz[i];
        if (g==1)
        {
            int qw;
            if (mid>mxz)
                qw=mxz; else
                qw=mid;
            s.insert(mp(qw,i));
            al[i]=true;
            continue;
        }
        ll o=mid;
        for (int j=20;j>=0;j--)
            if (f[g][j] && f[g][j]!=1 && len[g]-len[f[g][j]]<=o)
            {
                o-=len[g]-len[f[g][j]];
                g=f[g][j];
            }
        if (o<=len[g])
            al[i]=false,tag[g]=true; else
            {
                al[i]=true;
                if (o>len[g]+mxz)
                    o=len[g]+mxz;
                int qw=o-len[g];
                mn[g]=min(mn[g],qw);
                t[g].insert(mp(qw,i));
                s.insert(mp(qw,i));
            }
    }
    judge(1);
    if (!s.empty())
    {
        multiset< pr > :: iterator it=s.end();
        it--;
        for (;it!=s.begin();it--)
            q[++q0]=*it;
        q[++q0]=*it;
    }
    for (int i=fr[1];i;i=nxt[i])
    {
        int v=d1[i];
        int cost=d2[i];
        if (wro[v])
            xx[++x0]=mp(cost,v);
    }
    if (q0<x0)
        return false;
    sort(xx+1,xx+x0+1,cmp);
    int l1=1,l2=1;
    while (l2<=x0)
    {
        int y=xx[l2].second;
        bool flag=false;
        for (multiset< pr > :: iterator it=t[y].begin();it!=t[y].end();++it)
            if (al[(*it).second])
            {
                al[(*it).second]=false;
                while (l1<=q0 && !al[q[l1].second])
                    l1++;
                l2++;
                flag=true;
                break;
            }
        if (flag)
            continue;
        if (q[l1].first<xx[l2].first)
            return false;
        l2++;
        if (l2>x0)
            return true;
        al[q[l1].second]=false;
        l1++;
        while (l1<=q0 && !al[q[l1].second])
            l1++;
        if (l1>q0)
            return false;
    }
    return true;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
        scanf("%d",&kz[i]);
    dfs(1);
    for (int i=fr[1];i;i=nxt[i])
        mxz=max(mxz,d2[i]);
    for (int j=1;j<=20;j++)
        for (int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
    r+=mxz;
    ans=-1;
    while (l<=r)
    {
        ll mid=(l+r) >> 1;
        if (check(mid))
        {
            ans=mid;
            r=mid-1;
        } else
            l=mid+1;
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-09-07 17:48  GK0328  阅读(116)  评论(0编辑  收藏  举报