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

题目传送门

题意太长就不给了


发现答案具有单调性(额外的时间不会对答案造成影响),故考虑二分答案。

贪心地想,在二分了一个时间之后,军队尽量往上走更好。所以我们预处理倍增数组,在二分时间之后通过倍增看某一个军队能到达的深度最低的点。接着,我们发现有一些军队可以到达根节点,还有额外的时间去到别的子树上,而有一些子树没有被封闭完全。这个时候需要我们利用贪心思想来分配军队。

我们将能到达根节点的军队剩余的时间记录下来,并将军队由哪一棵子树而来记录下来,将其按照剩余时间从大到小排序。接着我们处理出没有完全封闭的子树数量,并将它们按照到根节点的边权大小从大到小排序。可以考虑到在分配某一棵子树的时候,一定是在满足条件的情况下选择剩余时间更少的更优。

而满足条件的军队有两种情况:①剩余时间大于等于该子树根节点到根的边权;②原来就由该棵子树来到根节点。对于①情况我们可以直接用指针处理,但是对于②并不是很好处理。我们可以如下处理:对剩余时间不满足条件的军队按照子树来源开个桶,每一次指针移动完毕之后,如果当前子树对应的桶中有军队,就直接拿出这个军队驻守这棵子树,并将这个桶中元素-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 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/9781085.html