NOIP 2012 疫情控制

题目描述

H 国有 n个城市,这 n个城市用n−1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。node

H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市创建检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也能够创建检查点。但特别要注意的是,首都是不能创建检查点的。c++

如今,在 H 国的一些城市中已经驻扎有军队,且一个城市能够驻扎多个军队。一支军队能够在有道路链接的城市间移动,并在除首都之外的任意一个城市创建检查点,且只能在一个城市创建检查点。一支军队通过一条道路从一个城市移动到另外一个城市所须要的时间等于道路的长度(单位:小时)。spa

请问最少须要多少个小时才能控制疫情。注意:不一样的军队能够同时移动。code

输出格式:

一个整数,表示控制疫情所须要的最少时间。若是没法控制疫情则输出−1blog

对于 100%的数据,2≤m≤n≤50,000,0<w <10^9排序

Solution:

一眼看过去,是一个最小化最大值的问题,因此能够考虑二分答案?ip

单调性显然。it

那么问题就是,咱们如今有了一个上限时间mid,军队必须默契配合,在mid时间内控制整个国家。io

怎么默契配合呢???
发现一个关键点,军队要控制的本质是一个子树的全部叶子。
class

那么,一个军队若是往上走,必定不会控制地更少,只可能控制地更多!!!

因此,军队只要疯狂的尽量地往上走,那么就多是这个军队在mid内的最优策略了!!

 

可是棘手的问题是,一个军队可能会跨过首都进入另外一棵子树。

可是显然的,进入另外一棵子树,必定就在这个子树的根部呆着不动了。

先采用两个倍增,mlogn找到每一个军队向上最多能到哪里(钦定不能到根)。

把这个点标记封锁。dfs一遍处理封锁标记的上传。

 

把停留在根的儿子的军队加入一个vector

vector按照剩余时间排序,

若是须要封锁x儿子,那么找一个剩余距离最小的军队,干脆就呆在x好了。

剩下的、能到根的军队的剩余时间加入一个set

 

暴力枚举根的每一个没有封锁的儿子,把第一个大于这个边权的剩余时间(lower_bound一下)匹配上。

找不到第一个大于这个边权的时间就return false

 

诶?这个样子有80分。

为何没有AC?

 

由于还有一种状况。

以前咱们说,“若是须要封锁x儿子,那么找一个剩余距离最小的军队,干脆就呆在x好了。

可是,若是x只有这一个军队,而且这个军队还能走很远。可是就卡死在这一步了。

其实,假如x到根的路径不是很长,可让x出去,填到y,再把子树z的某个军队出来,填到x

多是更优的!!

 

因此,脑残地,作两遍,一次是上面说的判断方法,。

还有一次,把全部的根的儿子节点,能到根都到根,再依次考虑下去。

 

可是显然错误。

由于, 第一次没有考虑跨子树,第二次所有跨子树了><

可是noip数据太水,AC了~~~~~~~~~

(注意倍增的顺序!!,先路程加上dis[to][j],再跳过去to=fa[to][j])

 

#include<bits/stdc++.h>
using namespace std; typedef long long ll; const int N=50000+5; int n; int m; int st[N]; struct node{ int nxt,to; ll val; }e[2*N]; int hd[N],cnt; ll l,r; bool son[N]; void add(int x,int y,ll z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].val=z; hd[x]=cnt; } int fa[N][22]; ll dis[N][22]; ll ans=-1; bool exi[N]; bool tmp[N]; void dfs(int x){ for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa[x][0]) continue; fa[y][0]=x; dis[y][0]=e[i].val; if(x==1) son[y]=1; dfs(y); } } void dfs2(int x){ if(exi[x]) return; bool fl=true; bool has=false; for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa[x][0]) continue; has=true; if(!exi[y]){ dfs2(y); if(!exi[y]) fl=false; } } if(!has) fl=false; if(fl) exi[x]=1; } struct po{ int pos; ll re; };//in the son of root
bool cmp(po a,po b){ return a.re<b.re; } vector<po>mem; multiset<ll>rt; multiset<ll>::iterator it; bool che(ll mid){ //cout<<endl<<"bug "<<mid<<endl;
    bool orz1=true; bool orz2=true; memset(exi,0,sizeof exi); mem.clear(); rt.clear(); for(int i=1;i<=m;i++){ int to=st[i]; ll has=0; for(int j=19;j>=0;j--){ if(fa[to][j]&&fa[to][j]!=1&&has+dis[to][j]<=mid){ has+=dis[to][j]; to=fa[to][j];//1.倍增的顺序不能反了!! 
 } } //cout<<i<<" st "<<st[i]<<" : "<<to<<" "<<has<<endl;
        
        if(son[to]){ po ko; ko.pos=to,ko.re=mid-has; mem.push_back(ko); } else{//warning !! : no son of tree exi[] is true
            exi[to]=1; } } dfs2(1);  sort(mem.begin(),mem.end(),cmp); memcpy(tmp,exi,sizeof exi); for(int i=0;i<mem.size();i++){ po x=mem[i]; if(!exi[x.pos]){ exi[x.pos]=1; } else{ x.re-=dis[x.pos][0]; if(x.re>0){ rt.insert(x.re); } } } for(int i=hd[1];i;i=e[i].nxt){ int y=e[i].to; if(!exi[y]){ it=rt.lower_bound(e[i].val); if(it==rt.end()) { orz1=false;break;} rt.erase(it); } } if(orz1) return true; rt.clear(); memcpy(exi,tmp,sizeof tmp); for(int i=0;i<mem.size();i++){ po x=mem[i]; x.re-=dis[x.pos][0]; //cout<<x.pos<<" "<<x.re<<endl;
        if(x.re>0){ rt.insert(x.re); } else exi[x.pos]=1; } for(int i=hd[1];i;i=e[i].nxt){ int y=e[i].to; if(!exi[y]){ //cout<<" go "<<y<<endl;
            it=rt.lower_bound(e[i].val); if(it==rt.end()){ orz2=false;break;} rt.erase(it); } } if(orz2) return true; return false; } int main() { scanf("%d",&n); int x,y; ll z; for(int i=1;i<=n-1;i++){ scanf("%d%d%lld",&x,&y,&z); add(x,y,z);add(y,x,z); r+=z; } scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&st[i]); dfs(1); //cout<<" on the tree"<<endl;
    /*for(int i=1;i<=n;i++){ cout<<i<<" : "<<fa[i][0]<<" "<<dis[i][0]<<endl; }*/
    for(int j=1;j<=19;j++){ for(int i=1;i<=n;i++){ fa[i][j]=fa[fa[i][j-1]][j-1]; dis[i][j]=dis[i][j-1]+dis[fa[i][j-1]][j-1]; } } l=0; //printf("ahhahahaah %d\n",che(30));
    
    while(l<=r){ ll mid=(l+r)>>1; if(che(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%lld",ans); return 0; }

 

正解是这样判断的。

仍是全部的儿子处军队能到根的到根,不能到根的就exi[x]=1设立检查站

对于在根的,

而后对于全部  根到未封锁儿子的 边权,从大到小排序。

对于x子树,看根部是否存在一个军队,原来是从x出来的,而且是当前剩余距离最小的。

若是有,那么就反悔,让他不要到根好了。

正确性比较显然,首先必须原来是x里的,其次,是当前最不能走的一个,让它回去必定不劣,不然用一个能走的更远的封锁,可能不优。

 

不然,就让大于x边权的最小军队封锁x好了。

相关文章
相关标签/搜索