题目描述
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
输出格式:共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
输入输出样例
说明
【输入输出样例说明】
第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需时间为 3 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 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。
NOIP 2012 提高组 第二天 第三题
分析
首先本题明确我们要做什么。
1.我们会发现,离根节点越近的节点,控制的节点更多。所以由贪心的思想,所有的军队都要尽可能地往根节点走。
预处理倍增
2.我们是要求走的最远的军队所走的距离,最小化最大值。
二分答案
3.让军队倍增的往上跳,处理哪些点可以到根节点并且还有剩余时间。
4.寻找哪些未被控制的子树,注意:如果该节点的所有儿子都被控制了,那么就相当于该节点也被控制了。
5.将我们已经记录好了的可以到根节点的军队按照剩余时间从大到小排序。
将未被控制的子树按照到子树到根节点的距离从大到小排序。
然后依次处理未被控制的子树要由哪支军队来管辖。
贪心(最难得部分也是最重要的部分)
错误贪心:我先讲我一开始错误的贪心,先从大到小排序,如果一个子树上有一个军队从那里跳上来,我就先让他受自己的子树。但我后来发现,假如当前这个军队的剩余时间还有很多,他就可以去控制其他的节点,而让其他的军队来守护他的子树。正确贪心:也是先从大到小排序,如果当前军队到另一个子树上,而当前这个子树上有军队,我就先用这个子树上剩余时间最短的来控制它,再用继续循环,直到找到一个子树没有军队驻扎在那里的,就去控制它。显然这样的贪心肯定是对的。
本题差不多就是这样了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; ll kzj,pwq; ll last[500100],len,army[500010],dep[501010][20],fa[501000][20],n,m,sum[500010]; bool vis[501000]; ll viss[501000],re[501000],shu[500010]; struct node { ll to,next,w; }a[101000]; struct NODE { ll rest,id,visss,num; }q[501000],p[500100]; bool cmp(NODE a,NODE b) { return a.rest>b.rest; } void add(ll a1,ll a2,ll a3) { len++; a[len].to=a2; a[len].w=a3; a[len].next=last[a1]; last[a1]=len; } void dfs(ll x,ll father) { ll k=0; for(ll i=last[x];i;i=a[i].next) { ll to=a[i].to; if(to==father) continue; k++;sum[x]=k; fa[to][0]=x; dep[to][0]=a[i].w; dfs(to,x); } } void zuxian() { for(ll j=1;j<=19;j++) for(ll i=1;i<=n;i++) { fa[i][j]=fa[fa[i][j-1]][j-1]; dep[i][j]=dep[i][j-1]+dep[fa[i][j-1]][j-1]; } } void ok(ll x,ll father) { ll k=0; for(ll i=last[x];i;i=a[i].next) { ll to=a[i].to; if(to==father) continue; ok(to,x); if(vis[to]) k++; if(k==sum[x]) vis[x]=1; } } bool check(ll mid) { memset(shu,0,sizeof(shu)); memset(p,0,sizeof(p)); memset(q,0,sizeof(q)); memset(viss,0,sizeof(viss)); memset(re,0,sizeof(re)); memset(vis,0,sizeof(vis)); pwq=0,kzj=0; for(ll i=1;i<=m;i++) { ll t=army[i],num=0; for(ll j=19;j>=0;j--) { if(fa[t][j]>1&&num+dep[t][j]<=mid) num+=dep[t][j],t=fa[t][j]; } if(fa[t][0]==1&&num+dep[t][0]<mid) { q[++pwq].rest=mid-num-dep[t][0]; q[pwq].id=t; q[pwq].num=num; } else vis[t]=1; } ok(1,0); if(vis[1]==1) {return 1;} else { for(ll i=last[1];i;i=a[i].next) { ll to=a[i].to; if(!vis[to]) { p[++kzj].rest=a[i].w; p[kzj].id=to; } } sort(p+1,p+1+kzj,cmp); sort(q+1,q+1+pwq,cmp); for(int i=1;i<=pwq;i++) { int t=q[i].id; int num=q[i].num; if(!viss[t]) viss[t]=1,re[t]=mid-num-dep[t][0],shu[t]=i; else if(re[t]>mid-num-dep[t][0]) { re[t]=mid-num-dep[t][0]; shu[t]=i; } } if(kzj>pwq) return 0; ll ppp=1; for(ll i=1;i<=kzj;i++) { if(shu[p[i].id]&&!q[shu[p[i].id]].visss) { q[shu[p[i].id]].visss=1; continue; } while(q[ppp].visss==1) ppp++; if(ppp>pwq) return 0; if(q[ppp].rest<p[i].rest) return 0; q[ppp].visss=1; } } return 1; } int main() { cin>>n;ll ans=-1,x,y,z; for(ll i=1;i<n;i++) { scanf("%lld%lld%lld",&x,&y,&z); add(x,y,z); add(y,x,z); } cin>>m; for(ll i=1;i<=m;i++) scanf("%lld",&army[i]); dfs(1,0);zuxian(); ll l=0,r=1010000000; while(l<=r) { ll mid=(l+r)/2; if(check(mid)) r=mid-1,ans=mid; else l=mid+1; } cout<<ans; }