[LOJ2607]【NOIP2012】疫情控制
題意:
題目描述
H 國有n個城市,這n個城市用n-1 條雙向道路相互連通構成一棵樹,1 號城市是首都,也是樹中的根節點。
H 國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要註意的是,首都是不能建立檢查點的。
現在,在 H 國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連接的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等於道路的長度(單位:小時)。
請問最少需要多少個小時才能控制疫情。註意:不同的軍隊可以同時移動。
輸入格式
第一行一個整數n,表示城市個數。
接下來的n-1行,每行3個整數u,v,w,每兩個整數之間用一個空格隔開,表示從城市u到城市v有一條長為w的道路。數據保證輸入的是一棵樹,且根節點編號為1
接下來一行一個整數m,表示軍隊個數。
接下來一行m個整數,每兩個整數之間用一個空格隔開,分別表示這m個軍隊所駐紮的城市的編號。
輸出格式
共一行,包含一個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出-1。
數據範圍
洛咕,即官方數據範圍:
LOJ:
吐槽:
LOJ這數據……真的天坑我艹
看LOJ數據以為$O(n^2logn)$只有30很虛,結果去看官方數據有60……
然後我$O(nlogn)$的正解不僅要開longlong還被卡常卡成95,在洛咕秒過……
SBLOJ!
題解:
由於軍隊可以同時移動,所以題目要求的就是使最大值最小,這種最優化問題明顯二分答案;
一個結論是軍隊肯定離根節點越近控制的點越多,即深度越小越優,所以軍隊選擇的策略肯定是向上走;
這樣貪心的思路就是讓盡量多的軍隊走到根節點,然後走到那些沒有軍隊的根節點的子節點上,這樣子就控制了以那個子節點為根的整個子樹;
但是有些軍隊在時間限制內是走不到根節點的,所以要按照走不走得到根節點分類,如果走不到就留在能走到的最高的點,把這些點設為已被控制,走的到的暫時放在根節點,然後記錄這些軍隊是從根節點的哪個子節點
這時可以dfs一遍求出哪些點已經被控制了,註意如果一個點的所有子節點都被控制了那麽這個點也算被控制了,然後記錄下所有沒被控制的根節點的子節點;
按照剩余的時間給所有能到達根節點的軍隊排序,按照到根節點的距離給沒被控制的那些子節點排序,明顯剩余時間多軍隊的去支援到根節點距離遠的子節點是最優的;
但是還有個問題,就是有些軍隊去支援後自己來源的那個子節點反而沒有軍隊去控制;
因此排序時要從小到大,然後優先讓每個軍隊向下回到自己來源的那個子節點(這樣做就可以忽略上到根節點又回去的過程,所以時間肯定符合),否則去支援其他子節點,最後判斷能否控制所有子節點即可。
這裏快速求距離用樹上倍增實現。
講的比較復雜,代碼裏細節也很多,寫的時候要註意。
代碼:
(LOJ被卡常95分,不加讀入優化90分)
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #include<queue> 7 #define inf 10000000000000000 8 #define eps 1e-9 9 using namespace std; 10 typedef long long ll; 11 struct edge{ 12 int v,w,next; 13 }a[600001]; 14 struct node{ 15 ll v; 16 int id; 17 friend bool operator <(node a,node b){ 18 return a.v<b.v; 19 } 20 }ar[300001],nd[300001]; 21 int n,m,u,v,w,tot=0,cnt=0,_cnt=0,arm[300001],fa[300001][20],head[300001]; 22 ll ans=-1,l,r,sum=0,dis[300001][20]; 23 bool isin[300001]; 24 char buffer[1000010],*hd,*tl; 25 inline char Getchar(){ 26 if(hd==tl){ 27 int len=fread(buffer,1,1000000,stdin); 28 hd=buffer,tl=hd+len; 29 if(hd==tl) 30 return EOF; 31 } 32 return *hd++; 33 } 34 inline int rd(){ 35 register int x=0; 36 char c; 37 do c=Getchar(); 38 while(!isdigit(c)); 39 do{ 40 x=(x<<1)+(x<<3)+(c^48); 41 c=Getchar(); 42 }while(isdigit(c)); 43 return x; 44 } 45 void add(int u,int v,int w){ 46 a[++tot].v=v; 47 a[tot].w=w; 48 a[tot].next=head[u]; 49 head[u]=tot; 50 } 51 void dfs(int u,int ff,int ds){ 52 fa[u][0]=ff; 53 dis[u][0]=ds; 54 for(int i=1;i<=19;i++){ 55 fa[u][i]=fa[fa[u][i-1]][i-1]; 56 dis[u][i]=dis[u][i-1]+dis[fa[u][i-1]][i-1]; 57 } 58 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 59 int v=a[tmp].v; 60 if(v!=ff){ 61 dfs(v,u,a[tmp].w); 62 } 63 } 64 } 65 void dfstf(int u,int fa){ 66 bool t1=true,t2=false; 67 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 68 int v=a[tmp].v; 69 if(v!=fa){ 70 dfstf(v,u); 71 t1&=isin[v]; 72 t2=true; 73 } 74 } 75 if(t1&&t2&&u!=1)isin[u]=true; 76 } 77 bool chk(ll k){ 78 int nw,ret=1; 79 ll d=0; 80 for(int i=1;i<=n;i++)isin[i]=false; 81 cnt=_cnt=0; 82 for(int i=1;i<=m;i++){ 83 nw=arm[i]; 84 d=0; 85 for(int j=19;j>=0;j--){ 86 if(fa[nw][j]&&d+dis[nw][j]<=k){ 87 d+=dis[nw][j]; 88 nw=fa[nw][j]; 89 } 90 } 91 if(nw!=1)isin[nw]=true; 92 else{ 93 int vv=arm[i]; 94 for(int j=19;j>=0;j--){ 95 if(fa[vv][j]>1)vv=fa[vv][j]; 96 } 97 ar[++cnt].v=k-d; 98 ar[cnt].id=vv; 99 } 100 } 101 dfstf(1,0); 102 for(int tmp=head[1];tmp!=-1;tmp=a[tmp].next){ 103 int v=a[tmp].v; 104 if(!isin[v]){ 105 nd[++_cnt].v=a[tmp].w; 106 nd[_cnt].id=v; 107 } 108 } 109 sort(ar+1,ar+cnt+1); 110 sort(nd+1,nd+_cnt+1); 111 nd[_cnt+1].v=inf; 112 for(int i=1;i<=cnt;i++){ 113 if(!isin[ar[i].id])isin[ar[i].id]=true; 114 else if(ar[i].v>=nd[ret].v)isin[nd[ret].id]=true; 115 while(isin[nd[ret].id])ret++; 116 } 117 return ret>_cnt; 118 } 119 int main(){ 120 memset(head,-1,sizeof(head)); 121 //scanf("%d",&n); 122 n=rd(); 123 for(int i=1;i<n;i++){ 124 //scanf("%d%d%d",&u,&v,&w); 125 u=rd(),v=rd(),w=rd(); 126 add(u,v,w); 127 add(v,u,w); 128 sum+=w; 129 } 130 //scanf("%d",&m); 131 m=rd(); 132 for(int i=1;i<=m;i++)arm[i]=rd();//scanf("%d",&arm[i]); 133 l=0,r=sum; 134 dfs(1,0,0); 135 while(l<=r){ 136 ll mid=(l+r)/2; 137 if(chk(mid))ans=mid,r=mid-1; 138 else l=mid+1; 139 } 140 printf("%lld",ans); 141 return 0; 142 }
[LOJ2607]【NOIP2012】疫情控制