noip2012 疫情控制
【问题描述】
H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
【输入】 输入文件名为blockade.in。
第一行一个整数n,表示城市个数。
接下来的n-1行,每行3个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市u到城市v有一条长为w的道路。数据保证输入的是一棵树,且根节点编号为1。
接下来一行一个整数m,表示军队个数。
接下来一行m个整数,每两个整数之间用一个空格隔开,分别表示这m个军队所驻扎的城市的编号。
【输出】 输出文件为blockade.out。
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
【输入输出样例】
blockade.in
4
1 2 1
1 3 2
3 4 3
2
2 2
blockade.out
3
【输入输出样例说明】
第一支军队在2号点设立检查点,第二支军队从2号点移动到3号点设立检查点,所需时间为3个小时。
【数据范围】
保证军队不会驻扎在首都。
对于20%的数据,2≤ n≤ 10;
对于40%的数据,2 ≤n≤50,0<w <105; 对于60%的数据,2 ≤ n≤1000,0<w <106; 对于80%的数据,2 ≤ n≤10,000;
对于100%的数据,2≤m≤n≤50,000,0<w <109。
/* 说一下这个题目的思考过程,首先,你要求一个最小控制的时间,在这一段时间内军队要不停的走,走到哪里与时间是有关的,而能否控制又与走到哪里有关,于是一个很自然的想法就是二分。于是问题变成了给定一个时间,检验在这些时间内是否能够控制所以节点 怎么检验?我们观察到一个事实,给你一些时间,你肯定要尽量往上走,越往上控制的路径就越多,这个走的过程我们用一个倍增的办法来处理。 由于根节点不能建立检查点,如果继续走肯定在往下走一步,那么是往下走还是停在原来的根节点的儿子上?在根节点怎么进行军队的分配? 考虑贪心处理,首先,如果确定一些军队是要去往下走,对于一个没有被控制的根子节点来说,一定是让剩余时间卡的最紧的人去处理他,有了这个思路再去想谁调到根上,谁留在原来的根子节点上,对应子如果已经被控制,一定要往根上跑,如果没有控制再讨论。如果我去了回不来,有可能别人也管不了,而他自己可能排不上用场,最优解可能减小,如果我去了可以回来,这个点在我分配的时候肯定会控制住,而且可能会用一个比较小的来控制使解更优 代码比较长,注意一些细节,我在网上找了很多题解都是90分,可能卡常吧,不管了 */ #include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<vector> #define ll long long #define fo(i,l,r) for(int i = l;i <= r;i++) #define fd(i,l,r) for(int i = r;i >= l;i--) #define gfo(i,l,r) for(int i = l;i < r;i++) #define gfd(i,l,r) for(int i = r;i > l;i--) using namespace std; const int maxn = 50050; ll read(){ ll x=0,f=1; char ch=getchar(); while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}; while(ch>='0'&&ch<='9'){x=x*10+(ch-'0');ch=getchar();}; return x*f; } struct edge{ int v; int nxt; ll w; }e[maxn]; struct dat{ int pos; ll rec; }; int n,m; int head[maxn],cnt,fa[maxn][25]; ll fa_d[maxn][25],d_son[maxn],tot; int tr[maxn],frm_son[maxn]; ll frm_d[maxn]; bool is_son[maxn],vis_now[maxn]; vector<ll> jd; vector<int> zq; vector<dat> dd; void ins(int u,int v,ll w){ cnt++; e[cnt].v = v; e[cnt].w = w; e[cnt].nxt = head[u]; head[u] = cnt; } void dfs(int x,int rec){ if(rec) frm_son[x] = rec; int to,lg = 0,j = x; while(fa[x][lg] && fa[fa[x][lg]][lg]){ fa[x][lg+1] = fa[fa[x][lg]][lg]; fa_d[x][lg+1] = fa_d[x][lg] + fa_d[fa[x][lg]][lg]; lg++; } for(int i = head[x];i;i = e[i].nxt){ to = e[i].v; if(to == fa[x][0]) continue; fa[to][0] = x; if(x == 1){ frm_d[to] = e[i].w; is_son[to] = true; rec = to; } fa_d[to][0] = e[i].w; dfs(to,rec); } } bool dfs2(int x){ if(vis_now[x]) return true; bool have_son = false; for(int i = head[x];i;i = e[i].nxt){ if(e[i].v == fa[x][0]) continue; have_son = true; if(!dfs2(e[i].v)) return false; } if(!have_son) return false; return true; } bool cmp(int a,int b){ return frm_d[a] < frm_d[b]; } bool check(ll t){ memset(vis_now,0,sizeof(vis_now)); dd.clear(); jd.clear(); zq.clear(); int now,lg; ll tmp; dat gg; fo(i,1,m){ now = tr[i]; lg = tmp = 0; while(lg >= 0){ if(fa[now][lg]&&tmp + fa_d[now][lg] <= t){ tmp += fa_d[now][lg]; now = fa[now][lg]; lg++; }else lg--; } if(is_son[now]){ vis_now[now] = true; }else if(now == 1 && tmp + frm_d[frm_son[tr[i]]] > t){ gg.pos = frm_son[tr[i]]; gg.rec = t-tmp; dd.push_back(gg); }else if(now == 1){ jd.push_back(t-tmp); }else{ vis_now[now] = true; } } for(int i = head[1];i;i = e[i].nxt){ if(dfs2(e[i].v))vis_now[e[i].v] = true; } for(int i = 0;i < dd.size();i++){ if(!vis_now[dd[i].pos]) vis_now[dd[i].pos] = true; else jd.push_back(dd[i].rec); } for(int i = head[1];i;i = e[i].nxt){ if(!vis_now[e[i].v]) zq.push_back(e[i].v); } sort(jd.begin(),jd.end()); sort(zq.begin(),zq.end(),cmp); int now_jd = 0,now_zq = 0; if(jd.size() < zq.size()) return false; while(now_zq < zq.size()){ if(now_jd >= jd.size()) return false; while(jd[now_jd] < frm_d[zq[now_zq]]){ now_jd++; if(now_jd >= jd.size()) return false; } now_zq++; now_jd++; } return true; } int main(){ n =read(); int u,v; ll w; fo(i,1,n-1){ u = read(); v = read(); w = read(); ins(u,v,w); ins(v,u,w); tot += w; } m = read(); fo(i,1,m){ u = read(); tr[i] = u; } dfs(1,0); ll lans = 0,rans = tot,mid,ans = -1; while(lans <= rans){ mid = (lans+rans)>>1; if(check(mid)){ ans = mid; rans = mid - 1; }else{ lans = mid + 1; } } cout<<ans; return 0; }
//题解
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <set> #include <map> #include <algorithm> #include <cmath> using namespace std; const int maxn = 100000; typedef long long LL; multiset<int> S; multiset<int>::iterator it; int n,u,v,w,m,nownode; LL f[maxn][20]; int y[maxn]; int g[maxn][20]; int sonTree[maxn]; int cnt[maxn]; int p[maxn]; int Min[maxn]; bool Leaf[maxn]; bool son[maxn]; struct Arm { int opt,time; }A[maxn]; struct Graph { int node[maxn * 2], len[maxn * 2], next[maxn * 2], v[maxn * 2]; int en; Graph(){ memset(v,0,sizeof(v)); memset(next,0, sizeof(next)); memset(len,0,sizeof(len)); en = 0; } void addEdge(int a,int b,int c) { en++; node[en] = b; len[en] = c; next[en] = v[a]; v[a] = en; en++; node[en] = a; len[en] = c; next[en] = v[b]; v[b] = en; } }G; void DFS(int x,int father) { sonTree[x] = nownode; bool flag= true; for (int j = G.v[x]; j; j = G.next[j]) { if (G.node[j] != father) { //father[j] = x; flag = false; f[G.node[j]][0] = G.len[j]; g[G.node[j]][0] = x; if (x==1) nownode = G.node[j]; DFS(G.node[j],x); } } Leaf[x] = flag; } void find_leaf(int x, int father) { if (cnt[x]>0) return ; if (Leaf[x]) { son[nownode] = true; return; } for (int j = G.v[x]; j ; j = G.next[j]) { if (G.node[j] != father) { if (x==1) nownode = G.node[j]; find_leaf(G.node[j], x); } } } bool ok(LL value) { S.clear(); int arm = 0; memset(cnt,0,sizeof(cnt)); memset(son,0,sizeof(son)); for (int i = 1; i <= m ;++i) { int len = value; int Pos = p[i]; for (int j = 16; j>=0; --j) { if (f[Pos][j]<=len && g[Pos][j]!=0) { len-=f[Pos][j]; Pos =g[Pos][j]; } } if (Pos == 1) { A[arm].opt = p[i]; A[arm++].time = len; } else cnt[Pos]++; } find_leaf(1,0); for (int i = 1;i <= n;++i) Min[i]= -1; for (int i = 0; i < arm ; ++ i) { if (son[sonTree[A[i].opt]]) { if (Min[sonTree[A[i].opt]] ==-1 || A[Min[sonTree[A[i].opt]]].time > A[i].time) Min[sonTree[A[i].opt]] = i; } } int tot = 0; for (int i = 1; i <= n;++i) { if (son[i] && Min[i] != -1 && A[Min[i]].time<f[i][0]) { A[Min[i]].time = -1; }else if (son[i]) y[tot++] = f[i][0]; } sort(y,y+tot); for (int i = 0; i < arm;++i) if (A[i].time != -1) S.insert(A[i].time); for (int i = tot-1; i>=0; --i) { if (S.lower_bound(y[i]) == S.end()) return false; it = S.lower_bound(y[i]); S.erase(it); } return true; } int main() { cin>>n; for (int i = 1;i < n;++ i) { scanf("%d %d %d",&u,&v,&w); G.addEdge(u,v,w); } cin>>m ; for (int i = 1;i <= m;++i) { scanf("%d", &p[i]); // cnt[p[i]] ++; } DFS(1,0); /*for (int i = 1;i <= n; ++i) { cout <<i<<" "<<f[i][0]<<" "<<g[i][0] << endl; }*/ for (int i = 1; i <=16; ++i) for (int j = 1; j <= n ;++j) { f[j][i] = f[j][i-1]+ f[g[j][i-1]][i-1]; g[j][i] = g[g[j][i-1]][i-1]; } LL L = 0 , R = (LL)n * round(10e9); int ans = -1; while (L <= R) { LL mid = (L+R) /2; if (ok(mid)) R = mid-1, ans = mid; else L = mid+1; } cout << ans << endl; return 0; }