您的位置:首页 > 其它

[NOIP2012][CODEVS1218]疫情控制(二分+倍增+贪心)

2016-08-23 18:47 483 查看

题目描述

传送门

题解

二分+判定,问题转化成了在mid的时间里能不能控制疫情。

关键是怎么check。

可以看出让所有的军队尽量向上走一定是最优的。可以倍增求出所有的军队在mid的时间里最多走到哪个点。

对于所有走不到根的军队,它们就只能停在那里了。对于所有走到根之后还有剩余时间的军队,可以继续走到根的别的儿子上,这些军队成为可以调动的军队。

枚举根的每一个儿子,如果这个儿子所代表的子树已经被控制了,即子树中所有走不到根的军队已经可以控制了这个子树,就不用管它。

如果它代表的子树没有被控制(注意这里不包括经过这个子树走到根的点),那么称这个点为需要调入的点。

所有可调动的军队都有一个剩余时间,所有需要调入的点都有一个需要时间(即根到儿子的边的权),将这两种点分别从小到大排序,贪心地分配,就可以达到目的。

需要注意的一点是,所有能走到根的军队一定是从根的某一个儿子走上来的,如果再让这个军队调去这个儿子的话,不能计算时间。具体做法是从小到大排序后,如果当前军队走上来的那个点没有被控制的话,就让这支军队去控制它走上来的那个点,否则的话,让它去控制它能控制的最小的点。

细节不好好想会出错的。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
#define N 50005
#define sz 16

int n,m,x,y,z,loc
,cnt; LL Max,ans;
int tot,point
,nxt[N*2],v[N*2]; LL c[N*2];
int f
[sz+5]; LL s
[sz+5];
bool vis
,leaf,has_res
;
struct hp
{
int pt,lastp; LL ti,leftime;
bool ctr;
}goal
,res
,save
;

inline void addedge(int x,int y,LL z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
inline void build(int x,int fa,int dep)
{
for (int i=1;i<sz;++i)
{
f[x][i]=f[f[x][i-1]][i-1];
s[x][i]=s[f[x][i-1]][i-1]+s[x][i-1];
}
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
f[v[i]][0]=x;
s[v[i]][0]=c[i];
build(v[i],x,dep+1);
}
}
inline hp multi(int x,LL len)
{
hp re;
LL sum=0;
for (int i=sz-1;i>=0;--i)
while (sum+s[x][i]<=len&&f[x][i]&&f[x][i]!=1)
sum+=s[x][i],x=f[x][i];

if (f[x][0]==1&&sum+s[x][0]<=len) re.pt=1,re.lastp=x,re.ti=sum+s[x][0],re.ctr=true,re.leftime=len-re.ti;
else re.pt=x,re.lastp=0,re.ti=sum,re.ctr=false,re.leftime=0;

return re;
}
inline void dfs(int x,int fa)
{
if (vis[x]) return;
bool flag=false;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
flag=true;
dfs(v[i],x);
}
if (!flag) leaf=false;
}
inline int cmp(hp a,hp b)
{
return a.leftime<b.leftime;
}
inline bool check(LL t)
{
int rcnt=0,scnt=0;
memset(vis,0,sizeof(vis));
for (int i=1;i<=m;++i)
{
goal[i]=multi(loc[i],t);
if (goal[i].ctr==false) vis[goal[i].pt]=true;
else save[++scnt]=goal[i];
}

memset(has_res,0,sizeof(has_res));
for (int i=point[1];i;i=nxt[i])
if (v[i])
{
leaf=true;
if (!vis[v[i]]) dfs(v[i],1);
if (!leaf) res[++rcnt].pt=v[i],res[rcnt].leftime=c[i];
else has_res[v[i]]=true;
}

if (scnt<rcnt) return false;

sort(save+1,save+scnt+1,cmp);
sort(res+1,res+rcnt+1,cmp);
int rnow=1,snow=1;
while (rnow<=rcnt)
{
if (has_res[res[rnow].pt])
{
rnow++;
continue;
}
if (snow>scnt) return false;
if (snow<=scnt&&!has_res[save[snow].lastp])
{
has_res[save[snow].lastp]=true;
snow++;
continue;
}
if (snow<=scnt&&save[snow].leftime<res[rnow].leftime) snow++;
else
{
has_res[res[rnow].pt]=true;
rnow++,snow++;
}
}
return true;
}
inline LL find()
{
LL l=0,r=Max,mid,ans=0;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int main()
{
freopen("blockade.in","r",stdin);
freopen("blockade.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d%I64d",&x,&y,&z);
addedge(x,y,z);
Max+=z;
}
scanf("%d",&m);
for (int i=1;i<=m;++i) scanf("%d",&loc[i]);

for (int i=point[1];i;i=nxt[i])
if (v[i]) ++cnt;
if (cnt>m)
{
puts("-1");
return 0;
}

build(1,0,1);
ans=find();
printf("%lld\n",ans);
}


总结

1、看来贪心的套路还是很深啊。

2、细节一定要想好了再写,不要写了错错了写,很浪费时间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: