其他分享
首页 > 其他分享> > luoguP1084疫情控制

luoguP1084疫情控制

作者:互联网

原题地址

题目分析

我们要明确我们做什么,一步一步慢慢来,否则会被这题逼疯。

1.预处理倍增

我们会发现,离根节点越近的节点,控制的节点更多。所以由贪心的思想,所有的军队都要尽可能地往根节点走。

”往上提“类型问题一般使用倍增优化。

好大的,那么我们可以dfs一遍,将倍增要用的一些值都处理好(见代码)

2.二分答案

军队可以同时移动,说明我们要控制传染病的时间是军队移动到位时,移动时间最长的军队的移动时间。而我们要求最小值,即要求最大化最小值。

二分答案一般用于求最大化最小值,最小化最大值。

所以就是二分啦,二分一个答案,事情就会更有方向。

3.”上提“军队

使用倍增的方法将军队在二分出的答案限制内尽力往上”提“,不过不可以到根节点。

4.处理剩余路程

如果当前军队可以到达根节点,那么记录一下它的编号和它到达根节点后还可以走的时间rest。如果这个军队i在根节点的子树x中,那么记录一下子树x的符合这个条件的点中,到根节点后剩余路程最短的点。

如果不可以到达,记录它被”提“到的节点被军队设置了检查点。

5.dfs找未被”封死“的子树

如果一个节点建立了检查点或者它的所有子树都设立了检查点,则说明以这个节点为根的子树已经被“封死”。记录根节点的所有子树中,未被“封死”的子树。

6.军队在子树间转移

将我们已经记录好了的可以到根节点的军队按照剩余路程从大到小排序。

将未被“封死”的子树按照到子树到根节点的距离从大到小排序。

然后依次处理未被“封死”的子树要由哪支军队来管辖。

当然离根节点远的军队由剩余路程大的军队来管辖是吼滴啦,不过缀吼滴还是就由本来就在这棵子树上的军队来管辖。所以我们先查看我们事先记录的(在子树x中,可以到达根节点,且到根节点后剩余路程最小的军队)是否被使用,如果被使用,再看当前没有被使用的军队里剩余路程最大的可否到达这棵子树。

这样我们就可以判断当前二分出的答案是否可行了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define LL long long
int read(){
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
    return q;
}
const int N=50005;
int n,m,tot,na,nb;
int h[N],ne[N<<1],to[N<<1];LL w[N<<1];
int f[N][18],army[N];LL dis[N][18];
void add(int x,int y,int z)
{to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
void dfs(int x,int las,LL havego){//1.预处理倍增
    f[x][0]=las,dis[x][0]=havego;
    for(int i=1;i<=17;++i){
        f[x][i]=f[f[x][i-1]][i-1];
        dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1];
    }
    for(int i=h[x];i!=-1;i=ne[i])
        if(to[i]!=las)dfs(to[i],x,w[i]);
}
struct node{LL rest;int id;}a[N],b[N];
int vis[N],used[N],restbj[N];LL restmin[N];
int checkok(int x,int las){//5.dfs找未被“封死”的子树
    int bj=1,i,bbj=0;
    if(vis[x])return 1;
    for(i=h[x];i!=-1;i=ne[i]){
        if(to[i]==las)continue;bbj=1;
        if(!checkok(to[i],x)){
            bj=0;
            if(x==1) b[++nb].id=to[i],b[nb].rest=w[i];
            else return 0;
        }
    }
    if(!bbj)return 0;
    return bj;
}
bool cmp(node x,node y){return x.rest>y.rest;}
int check(LL lim){
    int i,j,x,now;LL num;na=nb=0;
    for(i=1;i<=n;++i)vis[i]=restbj[i]=0;
    for(i=1;i<=m;++i)used[i]=0;
    for(i=1;i<=m;++i){
        x=army[i],num=0;
        for(j=17;j>=0;--j)//3.上提军队
            if(f[x][j]>1&&num+dis[x][j]<=lim)
            num+=dis[x][j],x=f[x][j];
        if(f[x][0]==1&&num+dis[x][0]<=lim){//4.处理剩余路程
            a[++na].rest=lim-num-dis[x][0],a[na].id=i;
            if(!restbj[x]||a[na].rest<restmin[x])
                restmin[x]=a[na].rest,restbj[x]=i;
        }
        else vis[x]=1;
    }
    if(checkok(1,0))return 1;
    sort(a+1,a+1+na,cmp),sort(b+1,b+1+nb,cmp);//6.军队在子树间转移
    now=1;used[0]=1;
    for(i=1;i<=nb;++i){
        if(!used[restbj[b[i].id]]){used[restbj[b[i].id]]=1;continue;}
        while(now<=na&&(used[a[now].id]||a[now].rest<b[i].rest))++now;
        if(now>na)return 0;used[a[now].id]=1;
    }
    return 1;
}
int main()
{
    int i,x,y,z;LL l=0,r=500000,mid,ans=-1;
    n=read();
    for(i=1;i<=n;++i)h[i]=-1;
    for(i=1;i<n;++i){
        x=read(),y=read(),z=read();
        add(x,y,z),add(y,x,z);
    }
    dfs(1,0,0);m=read();
    for(i=1;i<=m;++i)army[i]=read();
    while(l<=r){//2.二分答案
        mid=(l+r)>>1;
        if(check(mid))r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%lld",ans);
    return 0;
}
疫情控制

 

标签:控制,子树,疫情,int,luoguP1084,军队,mid,include,节点
来源: https://www.cnblogs.com/zhouxuanbodl/p/10805106.html