1. 程式人生 > >Luogu1084 NOIP2012D2T3 疫情控制 二分答案、搜索、貪心、倍增

Luogu1084 NOIP2012D2T3 疫情控制 二分答案、搜索、貪心、倍增

max 預處理 ++ clu out bfs 兩種 利用 bsp

題目傳送門

題意太長就不給了


發現答案具有單調性(額外的時間不會對答案造成影響),故考慮二分答案。

貪心地想,在二分了一個時間之後,軍隊盡量往上走更好。所以我們預處理倍增數組,在二分時間之後通過倍增看某一個軍隊能到達的深度最低的點。接著,我們發現有一些軍隊可以到達根節點,還有額外的時間去到別的子樹上,而有一些子樹沒有被封閉完全。這個時候需要我們利用貪心思想來分配軍隊。

我們將能到達根節點的軍隊剩余的時間記錄下來,並將軍隊由哪一棵子樹而來記錄下來,將其按照剩余時間從大到小排序。接著我們處理出沒有完全封閉的子樹數量,並將它們按照到根節點的邊權大小從大到小排序。可以考慮到在分配某一棵子樹的時候,一定是在滿足條件的情況下選擇剩余時間更少的更優。

而滿足條件的軍隊有兩種情況:①剩余時間大於等於該子樹根節點到根的邊權;②原來就由該棵子樹來到根節點。對於①情況我們可以直接用指針處理,但是對於②並不是很好處理。我們可以如下處理:對剩余時間不滿足條件的軍隊按照子樹來源開個桶,每一次指針移動完畢之後,如果當前子樹對應的桶中有軍隊,就直接拿出這個軍隊駐守這棵子樹,並將這個桶中元素-1,在之後的指針移動中不算在可分配的軍隊內。

這神題碼量真心很長

  1 #include<bits/stdc++.h>
  2 #define MAXN 50010
  3 #define int long long
  4 #define ld long double
  5
using namespace std; 6 7 inline int read(){ 8 int a = 0; 9 bool f = 0; 10 char c = getchar(); 11 while(!isdigit(c)){ 12 if(c == -) 13 f = 1; 14 c = getchar(); 15 } 16 while(isdigit(c)){ 17 a = (a << 3) + (a << 1) + (c ^ 0); 18 c = getchar();
19 } 20 return f ? -a : a; 21 } 22 23 struct Edge{ 24 int end , upEd , time; 25 }Ed[MAXN << 1]; 26 struct L{ 27 int ind , lTime; 28 bool operator < (L a){ 29 return lTime > a.lTime; 30 } 31 }now[MAXN]; 32 int que[MAXN] , nowDir[MAXN] , next[MAXN][21][2] , head[MAXN] , army[MAXN] , N , M , cntEd , cntNow , cntQue; 33 bool vis[MAXN]; 34 35 bool cmp(int a , int b){ 36 return next[a][0][1] > next[b][0][1]; 37 } 38 39 inline void addEd(int a , int b , int c){ 40 Ed[++cntEd].end = b; 41 Ed[cntEd].time = c; 42 Ed[cntEd].upEd = head[a]; 43 head[a] = cntEd; 44 } 45 46 void dfs(int now){ 47 for(int i = 1 ; i <= 20 ; i++){ 48 next[now][i][0] = next[next[now][i - 1][0]][i - 1][0]; 49 next[now][i][1] = next[now][i - 1][1] + next[next[now][i - 1][0]][i - 1][1]; 50 } 51 for(int i = head[now] ; i ; i = Ed[i].upEd){ 52 if(!next[Ed[i].end][0][0]){ 53 next[Ed[i].end][0][0] = now; 54 next[Ed[i].end][0][1] = Ed[i].time; 55 dfs(Ed[i].end); 56 } 57 } 58 } 59 60 inline int jump(int dir , int &k){ 61 for(int i = 20 ; i >= 0 ; i--) 62 if(next[dir][i][1] <= k && next[dir][i][0] != 1){ 63 k -= next[dir][i][1]; 64 dir = next[dir][i][0]; 65 } 66 return dir; 67 } 68 69 queue < int > q; 70 71 inline bool bfs(int dir){ 72 while(!q.empty()) 73 q.pop(); 74 vis[dir] = 1; 75 q.push(dir); 76 while(!q.empty()){ 77 int t = q.front(); 78 q.pop(); 79 if(Ed[head[t]].upEd == 0) 80 return 1; 81 for(int i = head[t] ; i ; i = Ed[i].upEd) 82 if(!vis[Ed[i].end]){ 83 q.push(Ed[i].end); 84 vis[Ed[i].end] = 1; 85 } 86 } 87 return 0; 88 } 89 90 inline bool check(int dir){ 91 cntNow = cntQue = 0; 92 memset(vis , 0 , sizeof(vis)); 93 memset(nowDir , 0 , sizeof(nowDir)); 94 vis[1] = 1; 95 for(int i = 1 ; i <= M ; i++){ 96 int k = dir , t = jump(army[i] , k); 97 if(next[t][0][1] <= k){ 98 now[++cntNow].ind = t; 99 now[cntNow].lTime = k - next[t][0][1]; 100 nowDir[t]++; 101 } 102 else 103 vis[t] = 1; 104 } 105 for(int i = head[1] ; i ; i = Ed[i].upEd) 106 if(!vis[Ed[i].end] && bfs(Ed[i].end)) 107 que[++cntQue] = Ed[i].end; 108 sort(que + 1 , que + cntQue + 1 , cmp); 109 sort(now + 1 , now + cntNow + 1); 110 int dik = 1 , cnt = 0; 111 for(int i = 1 ; i <= cntQue ; i++){ 112 while(dik <= cntNow && now[dik].lTime >= next[que[i]][0][1]){ 113 if(nowDir[now[dik].ind]){ 114 nowDir[now[dik].ind]--; 115 cnt++; 116 } 117 dik++; 118 } 119 if(nowDir[que[i]]) 120 nowDir[que[i]]--; 121 else 122 if(cnt) 123 cnt--; 124 else 125 return 0; 126 } 127 return 1; 128 } 129 130 main(){ 131 next[1][0][0] = 1; 132 N = read(); 133 for(int i = 1 ; i < N ; i++){ 134 int a = read() , b = read() , c = read(); 135 addEd(a , b , c); 136 addEd(b , a , c); 137 } 138 dfs(1); 139 M = read(); 140 for(int i = 1 ; i <= M ; i++) 141 army[i] = read(); 142 int L = 0 , R = 1e14 + 1; 143 while(L < R){ 144 int mid = L + R >> 1; 145 if(check(mid)) 146 R = mid; 147 else 148 L = mid + 1; 149 } 150 if(L == 1e14 + 1) 151 cout << -1; 152 else 153 cout << L; 154 return 0; 155 }

Luogu1084 NOIP2012D2T3 疫情控制 二分答案、搜索、貪心、倍增