刷题总结——疫情控制(NOIP2012提高组)
题目:
题目背景
NOIP2012 提高组 DAY2 试题。
题目描述
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入格式
第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
输出格式
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
样例数据 1
备注
【样例说明】
第一支军队在 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。
题解:
贪心的妙用····表示一道题先根据题意找到基本策略真的很重要
首先要明白,将军队在不超时的情况下往根节点移动越多控制的叶子节点肯定是越多的。
因此先二分答案,然后在答案的限制下尽量将所有军队往根节点移动(因为一个军队越靠近根节点控制的叶节点肯定越多)
然后通过递归(注意这里递归是如果一个节点的所有儿子都被军队占领了,相当于它一个节点被占领,这样递归从叶子节点到根节点层层染色)找到根节点(1号)的哪些儿子还需要军队来占领(因为如果根节点的儿子还未被占领,则儿子所在子树的叶子节点肯定还未被控制)
于是那些移动得到根节点的军队就有用处了···首先,如果一个军队在移动到根节点的过程中经过了这些还未被占领的儿子节点中的一个,则用这个军队占领这个儿子节点即可.如果不是,将它们派去占领其他儿子节点(注意这里又会用到一次贪心,将移动到根节点后剩余时间少的军队尽量去占领那些与根节点距离小的儿子节点,总体下来无疑会占领得更多)。
这样的话所算出的占领叶子节点数一定是最多的
没想到这道题考贪心会考这么深···
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; const int N=5e4+5; int Rint() { char c; int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } long long Rlong() { char c; long long f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } struct node { int point; long long dis; }army[N],leftcity[N]; bool visit[N]; bool comp(node x,node y) { return x.dis<y.dis; } int first[N],next[N*2],go[N*2],tot,a[N]; long long val[N*2],dis[N][32]; int n,m,g[N][32],deep[N],tota,totl; void comb(int a,int b,long long c) { next[++tot]=first[a],first[a]=tot,go[tot]=b,val[tot]=c; next[++tot]=first[b],first[b]=tot,go[tot]=a,val[tot]=c; } inline void dfs(int u,int fa) { for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==fa) continue; g[v][0]=u; deep[v]=deep[u]+1; dis[v][0]=val[e]; dfs(v,u); } } inline void color(int u) { int t1=1,t2=0; for(int e=first[u];e;e=next[e]) { int v=go[e]; if(v==g[u][0]) continue; color(v); if(!visit[v]) t1=0; else t2=1; } if(t1==1&&t2==1&&u!=1) visit[u]=true; } inline bool check(long long limit) { memset(visit,false,sizeof(visit)); tota=0,totl=0; for(int i=1;i<=m;i++) { long long temp=limit; int u=a[i]; for(int j=30;j>=0;j--) { if(g[u][j]>=1&&temp>=dis[u][j]) { temp-=dis[u][j]; u=g[u][j]; if(u==1) break; } } if(u!=1) visit[u]=true; else { army[++tota].dis=temp; u=a[i]; for(int j=30;j>=0;j--) if(g[u][j]>1) u=g[u][j]; army[tota].point=u; } } color(1); for(int e=first[1];e;e=next[e]) { if(!visit[go[e]]) { leftcity[++totl].point=go[e]; leftcity[totl].dis=val[e]; } } sort(leftcity+1,leftcity+totl+1,comp); sort(army+1,army+tota+1,comp); leftcity[totl+1].dis=1e+15; int head=1; for(int i=1;i<=tota;i++) { if(!visit[army[i].point]) visit[army[i].point]=true; else { if(army[i].dis>=leftcity[head].dis) visit[leftcity[head].point]=true; } while(visit[leftcity[head].point]==true) { head++; if(head>totl) return true; } } return false; } int main() { //freopen("a.in","r",stdin); n=Rint(); int A,B; long long C; long long left=0,right=0; for(int i=1;i<n;i++) { A=Rint(),B=Rint(),C=Rlong(); comb(A,B,C); right+=C; } m=Rint(); dfs(1,0); for(int i=1;i<=m;i++) a[i]=Rint(); for(int i=1;i<=30;i++) for(int j=1;j<=n;j++) { g[j][i]=g[g[j][i-1]][i-1]; dis[j][i]=dis[j][i-1]+dis[g[j][i-1]][i-1]; } while(left<right) { long long mid=(left+right)/2; if(check(mid)) right=mid; else left=mid+1; } if(check(left)) cout<<left<<endl; else cout<<"-1"<<endl; return 0; }