题意太长就不给了
发现答案具有单调性(额外的时间不会对答案造成影响),故考虑二分答案。
贪心地想,在二分了一个时间之后,军队尽量往上走更好。所以我们预处理倍增数组,在二分时间之后通过倍增看某一个军队能到达的深度最低的点。接着,我们发现有一些军队可以到达根节点,还有额外的时间去到别的子树上,而有一些子树没有被封闭完全。这个时候需要我们利用贪心思想来分配军队。
我们将能到达根节点的军队剩余的时间记录下来,并将军队由哪一棵子树而来记录下来,将其按照剩余时间从大到小排序。接着我们处理出没有完全封闭的子树数量,并将它们按照到根节点的边权大小从大到小排序。可以考虑到在分配某一棵子树的时候,一定是在满足条件的情况下选择剩余时间更少的更优。
而满足条件的军队有两种情况:①剩余时间大于等于该子树根节点到根的边权;②原来就由该棵子树来到根节点。对于①情况我们可以直接用指针处理,但是对于②并不是很好处理。我们可以如下处理:对剩余时间不满足条件的军队按照子树来源开个桶,每一次指针移动完毕之后,如果当前子树对应的桶中有军队,就直接拿出这个军队驻守这棵子树,并将这个桶中元素-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 }