【倍增】【图论】NOIP2012 疫情控制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34454069/article/details/82494567

分析:

首先,求最大的最小,肯定用二分答案。

然后一个很显然的贪心是:在没有到达根节点的情况下,肯定越往上走越优秀。

所以可以通过树上倍增,得到每个军队在我们二分的答案范围内,能到达的最靠上的点(不能为根)。

现在可以先来一发树DP,统计哪些子树是已经合法了的。但现在有一些特殊的军队,就是已经到达了根节点的亲儿子,而且还能继续动的军队。这些军队的最终位置还没有固定,所以不能算进树DP里面。

现在考虑这些特殊军队怎么动。一个简单的方法是:首先把所有的军队移动到根节点,然后按照每支军队还能移动的距离排序。

然后对每个根节点的亲儿子也按照距离,从大到小排序。
从离根节点距离最远的亲儿子开始考虑。

首先,所有能到达这个点的军队,一定能到达比它后面考虑的儿子节点。
那么我们肯定从能到达这个儿子的军队中,随便拿一个走这里即可。

但是有一种特殊情况,就是如果有军队是从这个儿子走上去的,那么即使这军队不能走回来,但可以一开始就不上去,相当于说这种军队也能到达这个点。显然先用这种军队一定是最优的。所以要找到所有从这个点上去的军队,若其没有被安排,那么就可以让它不上根节点。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 50010
#define MAXANS 1000000000ll
using namespace std;
typedef long long ll;
typedef multiset<ll>::iterator sit;
typedef pair<int,int> pii;
vector<int> a[MAXN],w[MAXN];
vector<ll> ready[MAXN];
int ar[MAXN],m;
int fa[MAXN][20];
ll dist[MAXN];
void dfs(int x,int f){
    for(int i=0;i<a[x].size();i++){
        int u=a[x][i];
        if(f==u)
            continue;
        if(x!=1)
            fa[u][0]=x;
        else
            fa[u][0]=u;
        dist[u]=dist[x]+w[x][i];    
        dfs(u,x);
    }
}
int used[MAXN];
void dfs1(int x,int f){
    if(a[x].size()==1&&x!=1)
        return;
    int flag=1;
    for(int i=0;i<a[x].size();i++){
        int u=a[x][i];
        if(u==f)
            continue;
        dfs1(u,x);  
        flag=min(flag,used[u]);
    }
    used[x]=max(flag,used[x]);
}
pii p[MAXN];
int cnt;
multiset<ll> st;
int go_up(int x,ll maxd){
    int x1=x;
    for(int i=19;i>=0;i--)
        if(dist[x1]-dist[fa[x][i]]<=maxd)
            x=fa[x][i];
    return x;   
}
bool check(ll d){
    for(int i=0;i<a[1].size();i++)
        ready[a[1][i]].clear();
    memset(used,0,sizeof used);
    for(int i=1;i<=m;i++){
        int gt=go_up(ar[i],d);  
        if(fa[gt][0]==gt)
            ready[gt].push_back(d-dist[ar[i]]+dist[gt]);
        else
            used[gt]=1;
    }
    dfs1(1,0);
    if(used[1]==1)
        return 1;
    st.clear();
    for(int i=0;i<cnt;i++){
        int u=p[i].second;
        for(int j=0;j<ready[u].size();j++)
            st.insert(ready[u][j]-dist[u]);
    }
    for(int i=0;i<cnt;i++){
        int u=p[i].second;
        if(used[u]==1)
            continue;
        sit it=st.end();
        if(it==st.begin())
            return 0;
        it--;
        bool flag=0;
        for(int j=0;j<ready[u].size();j++)
            if(ready[u][j]-dist[u]<=*it){
                sit it1=st.lower_bound(ready[u][j]-dist[u]);
                st.erase(it1);
                flag=1;
                break;
            }
        if(flag==0){
            if(*it < dist[u])
                return 0;
            st.erase(it);   
        }
    }
    return 1;
}
int n,u,v,val;
bool cmp(int x,int y){
    return dist[x]<dist[y]; 
}
int main(){
    //freopen("blockade.in","r",stdin);
    SF("%d",&n);
    for(int i=1;i<n;i++){
        SF("%d%d%d",&u,&v,&val);    
        a[u].push_back(v);
        a[v].push_back(u);
        w[u].push_back(val);
        w[v].push_back(val);
    }
    SF("%d",&m);
    for(int i=1;i<=m;i++){
        SF("%d",&u);
        ar[i]=u;
    }
    sort(ar+1,ar+1+m,cmp);
    dfs(1,0);
    cnt=a[1].size();
    for(int i=0;i<cnt;i++)
        p[i]=make_pair(-dist[a[1][i]],a[1][i]);
    sort(p,p+cnt);
    for(int i=1;i<20;i++)
        for(int j=1;j<=n;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];
    ll l=1,r=MAXANS,ans=-1;
    while(l<=r){
        ll mid=(l+r)>>1ll;
        if(check(mid)){
            ans=mid;
            r=mid-1ll;
        }
        else
            l=mid+1ll;  
    }
    PF("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_34454069/article/details/82494567