【noip 2012】提高组Day2T3.疫情控制

Description

H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。 
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。 
现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。 
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

Input

第一行一个整数n,表示城市个数。 
接下来的n-1行,每行3个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市u到城市v有一条长为w的道路。数据保证输入的是一棵树,且根节点编号为1。 
接下来一行一个整数m,表示军队个数。 
接下来一行m个整数,每两个整数之间用一个空格隔开,分别表示这m个军队所驻扎的城市的编号。

Output

共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

Sample Input

4
1 2 1
1 3 2
3 4 3
2
2 2

Sample Output

3

HINT

保证军队不会驻扎在首都。 
对于20%的数据,2≤ n≤ 10; 
对于40%的数据,2 ≤n≤50,0<w <10^5; 
对于60%的数据,2 ≤ n≤1000,0<w <10^6; 
对于80%的数据,2 ≤ n≤10,000; 
对于100%的数据,2≤m≤n≤50,000,0<w <10^9。

A了这道题之后无意中发现可以把自己的代码hack掉(放出的代码是已经修改完的,啊当然有可能还会被hack掉)

以下是数据:2 1 2 5 1 2,应输出0。

这组数据也可以hack掉网上的很多题解……然而vijos和codevs都没卡。

方法是二分+贪心+拓扑排序/倍增,这里用的是拓扑排序。

贪心可得,每个军队尽量要往上走。二分出mid之后,走不到根节点的军队就走到他们所能到达的最靠近根节点的节点,能走到根节点的军队则扔进一个队列里,再将根节点尚未被控制的子节点扔进另一个队列,每个子节点需要一支军队控制。贪心可得,将军队走到根节点后的剩余时间从小到大sort一发,再按根节点到子节点所需时间将子节点sort一发,一一匹配即可。

详见代码。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<iostream>
  5 #define ll long long
  6 using namespace std;
  7 const int N=50010;
  8 int n,m,cnt,u,v,w,cnt1,cnt2,first[N];
  9 int army[N],q[N],fa[N],anc[N],t1[N],t2[N];
 10 //anc[i]代表控制节点i的根节点的子节点的编号
 11 bool notl[N],arr[N],mark[N];
 12 //arr[i]代表节点i的子树所包括的叶子结点是否全部被控制
 13 ll l,r,tot,dis[N],cost[N],t[N];
 14 //t[i]代表军队往上走到达节点i剩余时间最大值
 15 struct edge{int next,to,w;}e[N*2];
 16 int read()
 17 {
 18     int x=0,f=1;char c=getchar();
 19     while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
 20     while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
 21     return x*f;
 22 }
 23 void ins(int u,int v,int w)
 24 {
 25     cnt++;e[cnt].to=v;e[cnt].w=w;
 26     e[cnt].next=first[u];first[u]=cnt;
 27 }
 28 bool cmp1(int a,int b){return cost[a]<cost[b];}
 29 bool cmp2(int a,int b){return dis[army[a]]>dis[army[b]];}
 30 void bfs()
 31 {
 32     int h=1,t=0;q[++t]=1;
 33     while(h<=t)
 34     {
 35         int u=q[h++];
 36         notl[u]=false;
 37         for(int i=first[u];i;i=e[i].next)
 38         {
 39             int v=e[i].to;
 40             if(v==fa[u])continue;
 41             dis[v]=dis[u]+e[i].w;
 42             fa[v]=u;cost[v]=e[i].w;//cost[i]代表节点i到其父节点所需时间
 43             if(u!=1)anc[v]=anc[u];
 44             else anc[v]=v;
 45             q[++t]=v;
 46             notl[u]=true;
 47         }
 48     }
 49 }
 50 bool check(ll mid)
 51 {
 52     memset(t,-1,sizeof(t));
 53     for(int i=1;i<=n;i++)arr[i]=notl[i];
 54     for(int i=n;i;i--)//按bfs的顺序倒着处理
 55     {
 56         int u=q[i];
 57         if(mark[u]&&dis[u]>mid)t[u]=mid;
 58         if(t[fa[u]]<t[u]-cost[u])t[fa[u]]=t[u]-cost[u];
 59         if(t[u]>=0)arr[u]=true;
 60         if(!arr[u])arr[fa[u]]=false;
 61         //若军队能到达节点u或节点u的子树所包括的叶子结点已全部被控制,arr[u]=true
 62     }
 63     cnt1=cnt2=0;
 64     for(int i=first[1];i;i=e[i].next)
 65         if(!arr[e[i].to])t1[++cnt1]=e[i].to;//将根节点尚未被控制的子节点扔进队列
 66     for(int i=1;i<=m;i++)
 67         if(dis[army[i]]<=mid)t2[++cnt2]=i;//将所有可以使用的军队扔进队列
 68     if(cnt1==0)return true;//若已经全部控制,则直接返回true
 69     sort(t1+1,t1+cnt1+1,cmp1);
 70     sort(t2+1,t2+cnt2+1,cmp2);
 71     for(int i=1,j=1;i<=cnt2;i++)
 72     {
 73         if(!arr[anc[army[t2[i]]]])arr[anc[army[t2[i]]]]=true;
 74         else if(cost[t1[j]]+dis[army[t2[i]]]<=mid)arr[t1[j]]=true;
 75         while(j<=cnt1&&arr[t1[j]])j++;
 76         if(j>cnt1)return true;
 77     }
 78     return false;
 79 }
 80 int main()
 81 {
 82     n=read();
 83     for(int i=1;i<n;i++)
 84     {
 85         u=read();v=read();w=read();
 86         r+=w;ins(u,v,w);ins(v,u,w);
 87     }
 88     tot=r;
 89     m=read();
 90     for(int i=1;i<=m;i++)
 91         army[i]=read(),mark[army[i]]=true;
 92     bfs();
 93     while(l<=r)
 94     {
 95         ll mid=(l+r)/2;
 96         if(check(mid))r=mid-1;
 97         else l=mid+1;
 98     }
 99     if(l==tot+1)printf("-1");
100     else printf("%lld",l);
101     return 0;
102 }

时间: 2024-07-30 13:20:01

【noip 2012】提高组Day2T3.疫情控制的相关文章

洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从 首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是,首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以

NOIP 2012 提高组 DAY1 T2 国王游戏

题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍的最前面.排好队后,所有的大臣都会获得国王奖赏的若干金币,每 位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右 手上的数,然后向下取整得到的结果. 国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序, 使得获得奖赏最多的大臣,所获奖赏尽可能的少

扩展欧几里得模板(洛谷1082 同余方程NOIP 2012 提高组 第二天 第一题)

题目描述 求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解. 输入输出格式 输入格式: 输入只有一行,包含两个正整数 a, b,用一个空格隔开. 输出格式: 输出只有一行,包含一个正整数 x0,即最小正整数解.输入数据保证一定有解. 输入输出样例 输入样例#1: 3 10 输出样例#1: 7 说明 [数据范围] 对于 40%的数据,2 ≤b≤ 1,000: 对于 60%的数据,2 ≤b≤ 50,000,000: 对于 100%的数据,2 ≤a, b≤ 2,000,000,000

NOIP 2012 提高组第二试模拟赛 Solution

第一题 题意 数据范围 Solution 三分求下凹函数最值 1 #include <cstdio> 2 #include <queue> 3 #include <iostream> 4 using namespace std; 5 inline void read(int &k) 6 { 7 k=0;int f=1;char c=getchar(); 8 while (c<'0'||c>'9')c=='-'&&(f=-1),c=ge

NOIP 2014 提高组 题解

NOIP 2014 提高组 题解 No 1. 生活大爆炸版石头剪刀布 http://www.luogu.org/problem/show?pid=1328 这是道大水题,我都在想怎么会有人错了,没算法,直接模拟,别读错题. 1 int wn[5][5]={{2,0,1,1,0}, 2 {1,2,0,1,0}, 3 {0,1,2,0,1}, 4 {0,0,1,2,1}, 5 {1,1,0,0,2}}; 6 7 int n,na,nb; 8 int a[222],b[222]; 9 int s1,s

NOIP 2008提高组第三题题解by rLq

啊啊啊啊啊啊今天已经星期三了吗 那么,来一波题解吧 本题地址http://www.luogu.org/problem/show?pid=1006 传纸条 题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运的是,他们可以通过传纸条来进行交流.纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n).从小渊

NOIP 2006 提高组 t1 能量项链

题目描述 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记.因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量.如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(Mars单位),新产生的珠子的头标

模拟(玩具谜题NOIP 2016 提高组 Day 1 第一题vijos2003)

描述 小南有一套可爱的玩具小人,它们各有不同的职业. 有一天,这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外.如下图: 这时singer告诉小南一个谜题:“眼镜藏在我左数第3个玩具小人的右数第1个玩 具小人的左数第2个玩具小人那里.” 小南发现,这个谜题中玩具小人的朝向非常关键,因为朝内和朝外的玩具小人的左右方向是相反的:面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向:而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向. 小

刷题总结——mayan游戏(NOIP2011提高组day2T3)

题目: 题目背景 NOIP2011提高组 DAY1 试题. 题目描述 Mayan puzzle 是最近流行起来的一个游戏.游戏界面是一个 7 行 5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上.游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下: 1.每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见输入输出样例说明中的图6 到图