[noip2012]疫情控制
Posted kgxw0430
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[noip2012]疫情控制相关的知识,希望对你有一定的参考价值。
noip2012
- 都这个时候了我居然还在水noip题,而且写了一上午(看题解看代码)终于改对233。
- 题意简述:有(m)支军队,每个军队在某一个点上。如果某一个点上有军队,那么这个点可以建立检查站。检查站的子树上所有点都可以被覆盖。问,最短需要多长时间,所有的叶子节点都能被覆盖。(注意,根节点不可以建立检查站)。
- 直接求不好求,答案显然具有单调性,先二分答案。
- 首先,每一个点在到达根节点之前,向上爬一定最优。如果一个点爬到1号点还有剩余时间,那么把它入栈。它们用来解决什么情况吧?就是1的儿子里可能某棵子树上有的叶子节点未被覆盖。
- dfs看一下哪些儿子(1的儿子)的子树里还有未被覆盖的叶子节点,这些点需要用栈里的点来覆盖。
- 怎样覆盖是最优方案?借助贪心的思路,我们把栈中的点和需要被覆盖的点,按照距离从大到小排序。假如当前处理到了第(i)个点,即(1-(i-1))的点都已经覆盖过了,如果这个点可以回去,(即这个点原本可以被覆盖,只不过我们让他跑到了1号节点上),就让他回去。否则,拿当前的栈中从左到右第一个没被使用过的点尝试覆盖当前点,如果不能覆盖,说明无解。
- 这样一定是最优的覆盖方案,思考为什么?
- 假如此时我们要覆盖(i),栈中的点我们使用到了第(c)个。如果此时(i)可以回去,说明(i)这个点在栈中,并且它一定在(c)的后面。这时候我们让他回去,相当于用了一个小的代价代替大的代价,帮我们覆盖了这个点。(c)的这个步数更多的点,就可以尝试去覆盖后面不容易覆盖的点。因此,贪心策略是对的。
注意数组范围等细节处理。
Coding
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e5+100;
ll n,m,tot,a[N],rd[N],v[N],ver[N<<1],Next[N<<1],lin[N],edge[N],f[N][35];ll g[N][35];
ll used[N],top1,top2;
ll zx[N];ll rx[N];
bool flag;
struct node{
ll rest;ll id;
}c[N],d[N];
ll read(){
char ch=getchar();ll num=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
return num*f;
}
void add(ll x,ll y,ll z){ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;edge[tot]=z;rd[y]++;}
bool cmp(node x,node y){return x.rest>y.rest;}
void dfs(ll x,ll fa,ll id){
f[x][0]=fa;g[x][0]=edge[id];
for(ll i=1;i<=20;++i) f[x][i]=f[f[x][i-1]][i-1],g[x][i]=(ll)(g[x][i-1]+g[f[x][i-1]][i-1]);
for(ll i=lin[x];i;i=Next[i]){
ll y=ver[i];
if(y==fa) continue;
dfs(y,x,i);
}
}
void Dfs(ll x,ll fa,ll now){
if(v[x]) {now=1;return;}
if(now==0&&rd[x]==1){ flag=0;return ;}
for(ll i=lin[x];i;i=Next[i]){
ll y=ver[i];
if(y==fa) continue;
Dfs(y,x,now);
}
}
bool check(ll mid){
memset(v,0,sizeof(v));
memset(rx,0,sizeof(rx));
memset(zx,0,sizeof(zx));
memset(used,0,sizeof(used));
top1=0,top2=0;
for(ll i=1;i<=m;++i){
ll x=a[i],num=0;
for(ll j=20;j>=0;--j)//3.上提军队
if(f[x][j]>1&&num+g[x][j]<=mid)
num+=g[x][j],x=f[x][j];
if(f[x][0]==1&&num+g[x][0]<=mid){//4.处理剩余路程
c[++top1].rest=mid-num-g[x][0],c[top1].id=i;
if(!zx[x]||c[top1].rest<rx[x])
rx[x]=c[top1].rest,zx[x]=i;
}
else v[x]=1;
}
for(ll i=lin[1];i;i=Next[i]){
ll y=ver[i];
flag=1;Dfs(y,1,0);
if(!flag) d[++top2].rest=edge[i],d[top2].id=y;
}
sort(c+1,c+top1+1,cmp);
sort(d+1,d+top2+1,cmp);
ll now=1;used[0]=1;
for(ll i=1;i<=top2;++i){
if(!used[zx[d[i].id]]&&zx[d[i].id]){used[zx[d[i].id]]=1;continue;}
while(now<=top1&&(used[c[now].id]||c[now].rest<d[i].rest))++now;
if(now>top1)return 0;used[c[now].id]=1;
}
return 1;
}
int main(){
//freopen("1.in","r",stdin);
n=read();//memset(g,0x3f,sizeof(g));
for(ll i=1;i<n;++i){
ll x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
m=read();
for(ll i=1;i<=m;++i) a[i]=read();
dfs(1,0,0);
ll l=0,r=1e15;
while(l+1<r){
ll mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
if(check(l)) printf("%lld
",l);
else printf("%lld
",r);
return 0;
}
以上是关于[noip2012]疫情控制的主要内容,如果未能解决你的问题,请参考以下文章