luogu 1084 疫情控制
分析
时隔多年再一次拿起这道题
求能控制疫情时,用时最长的的军队的时间最短,
一般二分
发现军队所用时间与距离成正比
而能控制的人数也大致随之单调
嗯,铁定二分
为什么?
明显,父亲控制的节点绝对不小于儿子
尽量把军队往上走就好
首都不能放军队
能够跳到首都的,要么就留在自己这个子树,要么去控制其他子树
废话
先染色,把没有覆盖完的子树的根(首都的儿子)处理出来
把可以跳到首都的军队及其子树记录下来
然后,贪心
明显,先将剩余时间短的军队派出去,方案会更优,剩余时间大的作用范围更大,选择余地更多
剩余时间短的,配对距离短的
这题,细节挺多
代码
1 /********************* 2 User:Mandy.H.Y 3 Language:c++ 4 Problem:luogu 1084 5 Algorithm: 6 *********************/ 7 8 #include<bits/stdc++.h> 9 10 using namespace std; 11 12 const int maxn = 5e4 + 5; 13 14 int n,size,first[maxn],cnt; 15 int m,father[maxn][18]; 16 long long dis[maxn][18]; 17 bool vis[maxn]; 18 long long sumw = 0; 19 int army[maxn]; 20 int anc[maxn];//表示这颗子树的根是首都的哪个儿子 21 22 struct Node{ 23 int res,anc; 24 }cur[maxn]; 25 26 struct Edge{ 27 int v,w,nt; 28 }edge[maxn << 1],ee[maxn]; 29 30 template<class T>inline void read(T &x){ 31 x = 0;bool flag = 0;char ch = getchar(); 32 while(!isdigit(ch)) flag |= ch == '-',ch = getchar(); 33 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 34 if(flag) x = -x; 35 } 36 37 template<class T>void putch(const T x){ 38 if(x > 9) putch(x / 10); 39 putchar(x % 10 | 48); 40 } 41 42 template<class T>void put(const T x){ 43 if(x < 0) putchar('-'),putch(-x); 44 else putch(x); 45 } 46 47 void file(){ 48 freopen("testdata(2).in","r",stdin); 49 // freopen("1084.out","w",stdout); 50 } 51 52 void eadd(int u,int v,int w){ 53 edge[++size].v = v; 54 edge[size].w = w; 55 edge[size].nt = first[u]; 56 first[u] = size; 57 } 58 59 void readdata(){ 60 read(n); 61 for(int i = 1;i < n; ++ i){ 62 int u,v,w; 63 read(u);read(v);read(w); 64 eadd(u,v,w); 65 eadd(v,u,w); 66 sumw += w; 67 } 68 read(m); 69 for(int i = 1;i <= m; ++ i ) read(army[i]); 70 } 71 72 void dfs(int u,int fa,int a){ 73 father[u][0] = fa; 74 anc[u] = a; 75 for(int i = 1;i <= 17; ++ i) 76 father[u][i] = father[father[u][i-1]][i-1], 77 dis[u][i] = dis[u][i-1]+dis[father[u][i-1]][i-1];//倍增存距离 78 for(int i = first[u];i;i = edge[i].nt){ 79 int v = edge[i].v; 80 int w = edge[i].w,ac; 81 if(v == fa) continue; 82 dis[v][0] = (long long)w; 83 if(!a) ac = v; 84 else ac = a; 85 anc[v] = ac;//主要是首都的儿子的根就是自己 86 dfs(v,u,ac); 87 } 88 } 89 90 void check_dfs(int u,int fa){ 91 vis[u] = vis[u] | vis[fa];//从父亲继承 92 bool judge = 1,son = 0; 93 for(int i = first[u];i;i = edge[i].nt){ 94 int v = edge[i].v; 95 if(v == fa) continue; 96 son = 1;//不是叶子 97 check_dfs(v,u); 98 if(!vis[v]) judge = 0; 99 //儿子都被覆盖完了,相当于这颗子树被覆盖 100 } 101 if(son) vis[u] |= judge;//不是叶子的节点才能用儿子标记 102 } 103 104 bool cmp(const Node &a,const Node &b){ 105 return a.res > b.res ; 106 } 107 108 bool cmp1(const Edge &a,const Edge &b){ 109 return a.w < b.w ; 110 } 111 112 bool check(long long x){ 113 memset(vis,0,sizeof(vis)); 114 cnt = 0; 115 for(int i = 1;i <= m; ++ i){ 116 int u = army[i]; 117 int fa = u; 118 long long res = 0; 119 for(int j = 17;j >= 0; --j){//倍增往上跳 120 if(father[u][j] && res+dis[u][j] <= x){ 121 //father不能是0 122 fa = father[u][j]; 123 res += dis[u][j]; 124 u = fa;//跳到不能跳为止 125 } 126 } 127 if(fa == 1){ 128 cur[++cnt].res = res; 129 cur[cnt].anc = anc[army[i]];//u已经改变了 ,可能变成1 130 } else vis[fa] = 1; 131 } 132 check_dfs(1,0);//标记已经被完全覆盖的子树 133 int num = 0; 134 for(int i = first[1];i;i = edge[i].nt){ 135 int v = edge[i].v; 136 int w = edge[i].w; 137 if(vis[v]) continue; 138 ee[++num].w = w; 139 ee[num].v = v; 140 } 141 sort(ee + 1,ee + num + 1,cmp1);//从小到大 142 sort(cur + 1,cur + cnt + 1,cmp);//从大到小 143 //用的时间长 = 剩余时间短 144 int j = 1; 145 for(int i = 1;i <= num; ++ i){//枚举没被覆盖的子树 146 if(vis[ee[i].v]) continue;//可能有些待在自己子树的已经被覆盖 147 while(ee[i].w + cur[j].res > x && ee[i].v != cur[j].anc && j <= cnt) { 148 vis[cur[j].anc] = 1;//不能去其他子树的,就待在自己子树就好 149 ++j; 150 } 151 if(j > cnt) break; 152 vis[ee[i].v] = 1; 153 ++j; 154 } 155 for(int i = first[1];i;i = edge[i].nt){ 156 int v = edge[i].v; 157 if(!vis[v]){ 158 return 0; 159 } 160 } 161 return 1; 162 } 163 164 void work(){ 165 dfs(1,0,0); 166 long long l,r,ans = 0; 167 l = 0,r = sumw; 168 while(l <= r){ 169 long long mid = (l + r) >> 1; 170 if(check(mid)) ans = mid,r = mid - 1; 171 else l = mid + 1; 172 } 173 put(ans); 174 } 175 176 int main(){ 177 // file(); 178 readdata(); 179 work(); 180 return 0; 181 }
非做顽石不可,哪管他敬仰暗唾