1. 程式人生 > >NOIP2012疫情控制(二分答案+樹上貪心)

NOIP2012疫情控制(二分答案+樹上貪心)

長度 deep ostream fine 最好的 連接 inline tor fin

H 國有n個城市,這 n個城市用n-1條雙向道路相互連通構成一棵樹,1號城市是首都,也是樹中的根節點。

H國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要註意的是,首都是不能建立檢查點的。

現在,在 H 國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連接的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等於道路的長度(單位:小時)。

請問最少需要多少個小時才能控制疫情。註意:不同的軍隊可以同時移動。

Solution

一眼望上去十分不可做,開始%大佬的博客。

發現正解是二分答案。

發現確實有單調性(有單調性一定要二分!!)

首都把整顆樹分成了好幾個部分。

對於每個軍隊,在不跳過首都的情況下,越靠上越優,所以我們把沒治軍隊暴力向上跳答案的高度。

但會有跳過的情況。。

跳到首都的軍隊,不會再跳到別的地方了,我們幹脆讓它跳到根節點下的那個點。

對於跳過的點,我們記下它在那個部分和它還能跳多遠,放到數組裏按距離從小到大排序。

遍歷這顆子樹,求出沒有被覆蓋完全的子樹根,放到數組/堆中從小到大排序。

掃一遍所有軍隊。

若軍隊所在子樹沒有被覆蓋,那我就守家(因為這是它最好的選擇)。

否則讓它去別的子樹。

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
#define N 60009
using namespace std;
int n,head[N],tot,p[N][22],deep[N],maxdeep,dep[N],top,m,w[N],tag[N],bu,ans;
bool has[N];
long long d[N][22
]; struct node{ int id,s; bool operator < (const node &b)const{ return s>b.s; } }; bool cmp(node a,node b){ return a.s<b.s; } node ve[N]; priority_queue<node>q; struct fd{ int n,to,l; }e[N<<1]; inline void add(int u,int v,int l){e[++tot].n=head[u];e[tot].to=v;e[tot].l=l;head[u]=tot;} int rd(){ int x=0; char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x; } void dfs(int u,int fa){ p[u][0]=fa;d[u][0]=deep[u]-deep[fa];maxdeep=max(maxdeep,deep[u]); for(int i=1;(1<<i)<=dep[u];++i) p[u][i]=p[p[u][i-1]][i-1],d[u][i]=deep[u]-deep[p[u][i]]; for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ int v=e[i].to; deep[v]=deep[u]+e[i].l; dep[v]=dep[u]+1; dfs(v,u); } } bool dfs3(int u,int fa){ if(has[u])return 0; bool tt=0; for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ if(dfs3(e[i].to,u))return 1; tt=1; } if(!tt)return 1; return 0; } bool check(int pos){ top=0; while(q.size())q.pop(); memset(has,0,sizeof(has)); for(int i=1;i<=m;++i){ int now=w[i],D=pos; for(int j=20;j>=0;--j) if(D>=d[now][j]&&p[now][j])D-=d[now][j],now=p[now][j]; if(now==1&&!D)has[tag[w[i]]]=1; if(now==1&&D)ve[++top]=node{tag[w[i]],D}; if(now!=1)has[now]=1; } sort(ve+1,ve+top+1,cmp); // for(int i=1;i<=top;++i)cout<<ve[i].s<<" "<<ve[i].id<<endl; // cout<<top<<endl; for(int i=head[1];i;i=e[i].n){ int v=e[i].to; if(dfs3(v,1))q.push(node{v,e[i].l}); else has[v]=1; } for(int i=1;i<=top;++i){ if(q.empty())return true; if(!has[ve[i].id])has[ve[i].id]=1; else{ node x=q.top(); while(q.size()&&has[x.id]){//care q.pop();x=q.top(); } if(has[x.id])return 1; if(x.s<=ve[i].s){ q.pop(); has[x.id]=1;//care } } } while(q.size()&&has[q.top().id])q.pop(); if(q.empty())return true; else return false; } void dfs2(int u,int fa){ tag[u]=bu;for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa)dfs2(e[i].to,u); } int main(){ n=rd(); int u,v,ww; for(int i=1;i<n;++i)u=rd(),v=rd(),ww=rd(),add(u,v,ww),add(v,u,ww); m=rd(); for(int i=1;i<=m;++i)w[i]=rd(); dfs(1,0); for(int i=head[1];i;i=e[i].n){ bu=e[i].to; dfs2(bu,1); } int l=0,r=maxdeep*2;//care ans=-1; while(l<=r){ int mid=(l+r)>>1; if(check(mid)){ ans=mid; r=mid-1; } else l=mid+1; } printf("%d\n",ans); return 0; }

NOIP2012疫情控制(二分答案+樹上貪心)