[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、细节一定要想好了再写,不要写了错错了写,很浪费时间。
相关文章推荐
- 洛谷1084/codevs1218 二分+倍增+贪心,疫情控制,分步讲解
- NOIP2012 疫情控制(二分,倍增,贪心)
- codevs1218: [NOIP2012]疫情控制
- Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)
- Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
- noip2012 疫情控制 图论+贪心+二分
- noip2012 疫情控制 (二分+倍增)
- codevs1218疫情控制--noip2012dayt3
- code vs 1218 疫情控制 (二分+贪心+倍增)
- [noip2012]疫情控制(二分+贪心)
- 【倍增】【set】[NOIP2012] codevs1199 开车旅行
- luogu1084疫情控制-二分+倍增+贪心
- 【贪心+高精度】NOIP2012D1T2国王游戏Codevs1198
- codevs 1218 疫情控制
- 【CodeVS 1218】【NOIP 2012】疫情控制
- 疫情控制(codevs 1218)
- 洛谷P1084:疫情控制 (二分答案+倍增+贪心)
- 疫情控制 2012年NOIP全国联赛提高组(二分答案+贪心)
- CODE[VS] 1198【NOIP2012】 国王游戏(贪心
- 基础算法(二分,贪心):NOIP 2012 疫情控制