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 }
View Code

 

posted @ 2019-09-11 11:33  Mandy_H_Y  阅读(233)  评论(0编辑  收藏  举报