洛谷P1084:疫情控制 (二分答案+倍增+贪心)
来源:互联网 发布:hecras软件 编辑:程序博客网 时间:2024/05/22 12:02
题目传送门:https://www.luogu.org/problem/show?pid=1084
题目分析:首先发现答案具有单调性,于是二分答案。二分了之后,发现每支军队肯定是往上走最优,于是预处理倍增,快速向上跳。接下来我们要处理的就是,如果一支军队可以走到根节点,该怎么做?
如果一棵子树中的所有叶子节点,到根的路径中都至少有一个军队,我们就称这棵子树是符合条件的。很明显,我们要将可以调到根节点的军队,转移到不符合条件的根节点的儿子。如果我们记到达根节点的军队i,还能再走的距离为rest[i];那么如果根的儿子u不合法,它在所有能到根的军队中找一个rest值刚好大于边(root,u)长度的军队,将这支军队转移到u,答案会最优。于是将所有rest值和边(root,u)从小到大排个序,扫一遍就行了。
但其实还有一个小细节:如果一支军队i是从节点u跳到根的,而u的子树本身就是不符合条件的,那么军队i驻扎到u,不需要考虑rest[i]和边(root,u)的长度关系。对于一个不符合条件的根节点的儿子u,考虑用它子树中跳上来的军队来驻扎:如果它子树中跳上来的军队rest的最小值,小于边(root,u)的长度,那么用这个军队驻扎在u会更优,因为它可以使一些rest值更大的军队去驻扎别的节点。
这样做时间复杂度就是
(写代码的时候有一个下标from[i]写成了i,码了个对拍调半天才调出来)
CODE:
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=50010;const int maxl=18;const long long oo=5e5;typedef long long LL;struct edge{ int obj,len; edge *Next;} e[maxn<<1];edge *head[maxn];int cur=-1;LL dis[maxn][maxl];int fa[maxn][maxl];bool Son[maxn];int dep[maxn];LL cnt[maxn];int from[maxn];int num;LL Min[maxn];int id[maxn];bool vis[maxn];bool full[maxn];int army[maxn];int n,m;void Add(int x,int y,int z){ cur++; e[cur].obj=y; e[cur].len=z; e[cur].Next=head[x]; head[x]=e+cur;}void Dfs1(int node){ for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if (son==fa[node][0]) continue; fa[son][0]=node; dis[son][0]=p->len; Dfs1(son); if (node==1) Son[son]=true,dep[son]=p->len; }}void Jump(int u,LL v){ for (int j=maxl-1; j>=0; j--) if ( dis[u][j]<=v && fa[u][j]!=1 ) v-=dis[u][j],u=fa[u][j]; if ( Son[u] && ((long long)dep[u])<v ) cnt[++num]=v-(long long)dep[u],from[num]=u; else vis[u]=true;}bool Dfs2(int node){ if (vis[node]) return true; bool f=true,leaf=true; for (edge *p=head[node]; p; p=p->Next) { int son=p->obj; if (son==fa[node][0]) continue; leaf=false; f&=Dfs2(son); } if (leaf) return false; return f;}bool Judge(LL v){ num=0; for (int i=1; i<=n; i++) Min[i]=oo+1LL,vis[i]=full[i]=false; for (int i=1; i<=m; i++) Jump(army[i],v); for (int i=1; i<=n; i++) if (Son[i]) full[i]=Dfs2(i); for (int i=1; i<=num; i++) if ( !full[ from[i] ] && cnt[i]<Min[ from[i] ] && cnt[i]<=((long long)dep[ from[i] ]) ) Min[ from[i] ]=cnt[i],id[ from[i] ]=i; for (int i=1; i<=n; i++) if (Min[i]<=oo) cnt[ id[i] ]=0; int x=0; for (int i=1; i<=n; i++) if ( Son[i] && !full[i] && Min[i]>oo ) id[++x]=dep[i]; if (!x) return true; if (!num) return false; sort(cnt+1,cnt+num+1); sort(id+1,id+x+1); int tail=1,sol=1; for (int i=1; i<=x; i++) { while ( tail<=num && cnt[tail]<((long long)id[i]) ) tail++; if (tail==num+1) { sol=0; break; } tail++; } return sol;}LL Binary(){ LL L=-1LL,R=oo+1LL; while (L+1LL<R) { LL mid=(L+R)>>1LL; if ( Judge(mid) ) R=mid; else L=mid; } return R;}int main(){ freopen("1084.in","r",stdin); freopen("1084.out","w",stdout); scanf("%d",&n); for (int i=1; i<=n; i++) head[i]=NULL; for (int i=1; i<n; i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); Add(x,y,z); Add(y,x,z); } scanf("%d",&m); for (int i=1; i<=m; i++) scanf("%d",&army[i]); fa[1][0]=1; Dfs1(1); for (int j=1; j<maxl; j++) for (int i=1; i<=n; i++) fa[i][j]=fa[ fa[i][j-1] ][j-1], dis[i][j]=dis[i][j-1]+dis[ fa[i][j-1] ][j-1]; LL ans=Binary(); if (ans<=oo) printf("%lld\n",ans); else printf("-1\n"); return 0;}
- 洛谷P1084:疫情控制 (二分答案+倍增+贪心)
- 洛谷 P1084 疫情控制 (二分答案+倍增+贪心)
- luogu1084【2012提高】疫情控制(二分答案+贪心+倍增)
- 疫情控制(二分+贪心+倍增)
- 洛谷 P1084 疫情控制
- 洛谷 P1084 疫情控制
- 洛谷 P1084 疫情控制
- 疫情控制 洛谷p1084
- 洛谷1084/codevs1218 二分+倍增+贪心,疫情控制,分步讲解
- code vs 1218 疫情控制 (二分+贪心+倍增)
- [NOIP2012][CODEVS1218]疫情控制(二分+倍增+贪心)
- NOIP2012 疫情控制(二分,倍增,贪心)
- luogu1084疫情控制-二分+倍增+贪心
- 洛谷P1084 疫情控制(NOIp2012)
- noip2012 疫情控制 (二分+倍增)
- 洛谷 P1084 [NOIP2012 D2T3] 疫情控制
- P1084 疫情控制
- P1084 疫情控制
- 多线程(一)
- openstack创建实例失败,bug(1)解决办法
- 【BzoJ 4719】【Noip2016】【天天爱跑步】【lca】【方程移项】【桶排优化】
- 第七周 项目四-队列数组
- 如何做好一个数据分析师?
- 洛谷P1084:疫情控制 (二分答案+倍增+贪心)
- 指针
- 使用字节流复制文件过程中容易导致复制的文件无法使用(如MP3文件比原来大了一千多倍且音乐无法播放)很严重的一个bug
- IFE-TASK11(任务十一:移动Web页面布局实践)
- 菜鸟先飞之JAVA_反射
- OpengGL
- java多线程中死锁实例
- Partition Equal Subset Sum问题及解法
- Matlab GUI图形化界面,坐标