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 = ;
ll read(){
ll x=,f=;
char ch=getchar();
while(!(ch>=''&&ch<='')){if(ch=='-')f=-;ch=getchar();};
while(ch>=''&&ch<=''){x=x*+(ch-'');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][];
ll fa_d[maxn][],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 = ,j = x;
while(fa[x][lg] && fa[fa[x][lg]][lg]){
fa[x][lg+] = fa[fa[x][lg]][lg];
fa_d[x][lg+] = 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][]) continue;
fa[to][] = x;
if(x == ){
frm_d[to] = e[i].w;
is_son[to] = true;
rec = to;
}
fa_d[to][] = 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][]) 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,,sizeof(vis_now));
dd.clear();
jd.clear();
zq.clear();
int now,lg;
ll tmp;
dat gg;
fo(i,,m){
now = tr[i];
lg = tmp = ;
while(lg >= ){
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 == && 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 == ){
jd.push_back(t-tmp);
}else{
vis_now[now] = true;
}
}
for(int i = head[];i;i = e[i].nxt){
if(dfs2(e[i].v))vis_now[e[i].v] = true;
}
for(int i = ;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[];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 = ,now_zq = ;
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,,n-){
u = read();
v = read();
w = read();
ins(u,v,w);
ins(v,u,w);
tot += w;
}
m = read();
fo(i,,m){
u = read();
tr[i] = u;
}
dfs(,);
ll lans = ,rans = tot,mid,ans = -;
while(lans <= rans){
mid = (lans+rans)>>;
if(check(mid)){
ans = mid;
rans = mid - ;
}else{
lans = mid + ;
}
}
cout<<ans;
return ;
}
//题解
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = ;
typedef long long LL;
multiset<int> S;
multiset<int>::iterator it;
int n,u,v,w,m,nownode;
LL f[maxn][];
int y[maxn];
int g[maxn][];
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 * ], len[maxn * ], next[maxn * ], v[maxn * ];
int en;
Graph(){
memset(v,,sizeof(v));
memset(next,, sizeof(next));
memset(len,,sizeof(len));
en = ;
}
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]][] = G.len[j];
g[G.node[j]][] = x;
if (x==) nownode = G.node[j];
DFS(G.node[j],x);
}
}
Leaf[x] = flag;
}
void find_leaf(int x, int father)
{
if (cnt[x]>) 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==) nownode = G.node[j];
find_leaf(G.node[j], x);
}
}
}
bool ok(LL value)
{
S.clear();
int arm = ;
memset(cnt,,sizeof(cnt));
memset(son,,sizeof(son));
for (int i = ; i <= m ;++i)
{
int len = value;
int Pos = p[i];
for (int j = ; j>=; --j)
{
if (f[Pos][j]<=len && g[Pos][j]!=)
{
len-=f[Pos][j];
Pos =g[Pos][j];
}
}
if (Pos == )
{
A[arm].opt = p[i];
A[arm++].time = len;
} else
cnt[Pos]++;
}
find_leaf(,);
for (int i = ;i <= n;++i) Min[i]= -;
for (int i = ; i < arm ; ++ i)
{
if (son[sonTree[A[i].opt]])
{
if (Min[sonTree[A[i].opt]] ==- ||
A[Min[sonTree[A[i].opt]]].time > A[i].time)
Min[sonTree[A[i].opt]] = i;
}
}
int tot = ;
for (int i = ; i <= n;++i)
{
if (son[i] && Min[i] != - && A[Min[i]].time<f[i][])
{
A[Min[i]].time = -;
}else
if (son[i]) y[tot++] = f[i][];
}
sort(y,y+tot);
for (int i = ; i < arm;++i)
if (A[i].time != -)
S.insert(A[i].time); for (int i = tot-; i>=; --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 = ;i < n;++ i)
{
scanf("%d %d %d",&u,&v,&w);
G.addEdge(u,v,w);
}
cin>>m ;
for (int i = ;i <= m;++i)
{
scanf("%d", &p[i]);
// cnt[p[i]] ++;
}
DFS(,);
/*for (int i = 1;i <= n; ++i)
{
cout <<i<<" "<<f[i][0]<<" "<<g[i][0] << endl;
}*/
for (int i = ; i <=; ++i)
for (int j = ; j <= n ;++j)
{
f[j][i] = f[j][i-]+ f[g[j][i-]][i-];
g[j][i] = g[g[j][i-]][i-];
} LL L = , R = (LL)n * round(10e9);
int ans = -;
while (L <= R)
{
LL mid = (L+R) /;
if (ok(mid)) R = mid-, ans = mid;
else L = mid+;
}
cout << ans << endl;
return ;
}
noip2012 疫情控制的更多相关文章
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...
- [NOIP2012]疫情控制 贪心 二分
题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...
- NOIP2012 疫情控制 题解(LuoguP1084)
NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...
- NOIP2012疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- [NOIP2012]疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- [NOIp2012]疫情控制 题解
好久没更,强迫自己写一篇. 神 tm 大预言家出的题 注意到如果 \(x\) 小时可以控制住疫情,则 \(\forall x'>x\) 必然也可以控制住疫情,显然答案具有单调性,可以二分答案. ...
- NOIP2012疫情控制(二分答案+树上贪心)
H 国有n个城市,这 n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示 ...
- noip2012疫情控制 题解
题目大意 给出一棵n个节点的树,根是1,要在除根节点以外的点建立检查点,使得从每条根到叶子的路径上都至少存在一个检查点.检查点由军队来建立.初始军队的位置是给定的,移动军队走一条边需要花费这条边的权值 ...
- luogu1084 [NOIp2012]疫情控制 (二分答案+倍增+dfs序)
先二分出一个时间,把每个军队倍增往上跳到不能再跳 然后如果它能到1号点,就记下来它跳到1号点后剩余的时间:如果不能,就让它就地扎根,记一记它覆盖了哪些叶节点(我在这里用了dfs序+差分,其实直接dfs ...
随机推荐
- POJ3154 Graveyard
Graveyard Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 1654 Accepted: 840 Specia ...
- Discuz X1.5 X2.5 X3 UC_KEY Getshell Write PHPCODE into config/config_ucenter.php Via /api/uc.php Vul
目录 . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 在Discuz中,uc_key是UC客户端与服务端通信的通信密钥.因此使用uc_k ...
- 大家是怎么做Code Review的?
先说说我们公司现在的做法,一个团队被人为地分为两个阵营:Senior Developers和Junior Developers,比例差不多是1:1,Senior Developers就担负着对Juni ...
- POJ 2559 Largest Rectangle in a Histogram(单调栈)
传送门 Description A histogram is a polygon composed of a sequence of rectangles aligned at a common ba ...
- [Android]关于Activity的InstanceState
Activity有两个方法onSaveInstanceState() 和 onRestoreInstanceState(). onSaveInstanceState()方法只适合用于保存一些临时性的状 ...
- 【转载】Linux启动过程
转自:http://cizixs.com/2015/01/18/linux-boot-process 简介 我们都知道:操作系统运行的代码是在硬盘上的,最终要跑到内存和 CPU 上,才能被我们使用. ...
- Mac下同时安装多个版本的JDK
JDK8 GA之后,小伙伴们喜大普奔,纷纷跃跃欲试,想体验一下Java8的Lambda等新特性,可是目前Java企业级应用的主打版本还是JDK6, JDK7.因此,我需要在我的电脑上同时有JDK8,J ...
- POJ 2892 Tunnel Warfare
传送门 很神奇的一道题,可以用线段树搞,为了练习treap所以拿treap写了. 其实根据询问,删除那个标号就加入平衡树,然后找到最大的和最小的就好了. 一些很烦人的小细节. //POJ 2892 / ...
- 堆优化的Dijkstra
SPFA在求最短路时不是万能的.在稠密图时用堆优化的dijkstra更加高效: typedef pair<int,int> pii; priority_queue<pii, vect ...
- GMap.NET使用一
https://greatmaps.codeplex.com/releases/view/20235 从上面网站下载需要的组件dll,也可以下载源码研究,解压后有两个文件夹,如图1所示,根据不同的fr ...