题目链接:https://www.luogu.org/problemnew/show/P1084

算法:倍增,二分答案,贪心 + 瞎搞。。

背景:上学长的数论课啥也听不懂,于是前去提高组找安慰。不巧碰上了这个题。。蒟蒻整整码了一天。。。调了又一天。。。。最后看的洛谷@beretty的题解。。。。。过了。。。。。。

感谢@beretty。

 警告!:代码量3K左右。


思路:

大概分析:

  首先,我们需要知道怎样才能控制疫情。(废话。。 )

  通过题意,可以想到在一棵根节点的子树中,越靠近根节点的节点能控制该棵树的叶子节点(边境城市)的个数越多。

(比如在 4 这个节点,可以控制 15 7 11 3 12 2 这几个叶子节点)

  ?? 这说明了什么 ??

  通过思考,我们知道如果样例合法,最坏情况也就是将检查站全部设在根节点的儿子上,便能控制疫情。

为何是二分答案?

  再来分析一下问题:军队可以同时移动,说明我们的答案是移动时间最长的军队的移动时间。而我们要求最小值,即最大化最小值。

  !!!最大化最小值!!!

  典型的二分答案的标志。      二分什么? 当然是时间。

如何check?

  上文中提到了(!敲黑板)  越接近根节点的节点控制能力越强。 这说明,我们只需要在规定时间内贪心的把军队向根节点移动,移动的越远越好(不到根节点都可以)。

  如何移动? 当然不能暴力的一个一个往上移。这时候 倍增 这种东西就特别好用。我们可以先预处理出来每个节点向上跳$2^j$步是谁,顺便处理出到这个点的距离。

  之前的预处理代码:(大佬们请跳过。。)

 void dfs(int p,int f)
{
for(int i=head[p];i;i=nex[i])
{
int v=to[i];
if(v!=f)
{
fa[v][]=p;
F[v][]=val[i];
dfs(v,p);
}
}
}

  倍增预处理代码:

 void update()
{
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
{
fa[i][j]=fa[fa[i][j-]][j-];
F[i][j]=F[i][j-]+F[fa[i][j-]][j-];
}
}

    我们可以让规定时间内能到根节点的部队先全部到根节点,顺便记录它在向上跳的路径中根节点的儿子 $top$ ,以及到根节点时的剩余时间 $rest$。不能到根节点的部队到它能到的地方,打上标记。

  重点!!! 有时候我们会发现,有的子树上没有军队(该子树暂时不能被控制),这时候就需要跨子树(越过根节点)移动部队。!!!这里好难想 可以将所有到根节点的部队按照 $rest$ 从小到大排序,然后看看它在 $rest$ 时间内能不能回到 $top$ ,如果不能,就不让它在根节点呆着,让它移动到 $top$ 去,控制该子树。

  操作完毕后,我们将那些没被控制的子树的 $top$ 存好,从大到小排序。再将现在仍在根节点的部队从大到小排个序,看看能否将没被控制的的全部控制。能,返回true,否则返回false。

  以下为全部代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = ;
const int root=;
const int logn = ;
int head[N];
int to[N<<];
int nex[N<<];
int val[N<<];
int cnt,n,m;
int fa[N][logn+];
int top[N];
int tdis[N];
int army[N];
long long F[N][logn+];
bool pos[N];
int q[N];
bool vis[N];
int cnt_a;
void addedge(int a, int b,int v)
{
nex[++cnt]=head[a];
head[a]=cnt;
to[cnt]=b;
val[cnt]=v;
}
void dfs(int p,int f)
{
for(int i=head[p];i;i=nex[i])
{
int v=to[i];
if(v!=f)
{
fa[v][]=p;
F[v][]=val[i];
dfs(v,p);
}
}
}
void update()
{
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
{
fa[i][j]=fa[fa[i][j-]][j-];
F[i][j]=F[i][j-]+F[fa[i][j-]][j-];
}
}
void dfs1(int p,int f,int topf,int dist)
{
top[p]=topf;
tdis[p]=dist;
bool ft=;
for(int i=head[p];i;i=nex[i])
{
int v=to[i];
if(v!=f)
{
ft=;
dfs1(v,p,topf,dist);
}
}
if(!ft)
pos[p]=;
}
bool fs;
void dfs2(int p,int f)
{
if(pos[p])
{
fs=;
return ;
}
for(int i=head[p];i;i=nex[i])
{
int v=to[i];
if(v!=f&&!vis[v])
{
dfs2(v,p);
if(fs)
return ;
}
}
}
bool check2(int p)
{
fs=;
dfs2(p,fa[p][]);
return fs;
}
struct node
{
int up;
int rest;
}stop[N];
bool cmp1(node a,node b)
{
return a.rest<b.rest;
}
bool cmp2(node a,node b)
{
return a.rest>b.rest;
}
bool cmp(int a,int b)
{
return a>b;
}
bool check(int time)
{
memset(stop,,sizeof(stop));
memset(vis,,sizeof(vis));
memset(q,,sizeof(q));
cnt_a=;
for(int i=;i<=m;i++)
{
int time2=time;
int now=army[i];
bool can=;
while()
{
for(int j=;j>=;j--)
{
if(fa[now][j]&&F[now][j]<=time2)
{
time2-=F[now][j];
now=fa[now][j];
break;
}
if(j==||now==)
{
can=;
break;
}
}
if(can)
break;
}
if(now==)
{
stop[++cnt_a].up=top[army[i]];
stop[cnt_a].rest=time2;
}
else
vis[now]=;
}
sort(stop+,stop+m+,cmp1);
for(int i=;i<=m;i++)
{
if(stop[i].rest<tdis[stop[i].up])
{
if(!vis[stop[i].up]&&check2(stop[i].up))
{
vis[stop[i].up]=;
stop[i].rest=-;
}
}
}
int tail=;
sort(stop+,stop+m+,cmp2);
for(int i=head[];i;i=nex[i])
{
int v=to[i];
if(!vis[v]&&check2(v))
q[++tail]=val[i];
}
sort(q+,q+tail+,cmp);
for(int i=;i<=tail;i++)
if(stop[i].rest<q[i])
return false;
return true;
}
int main()
{
int l, r;
scanf("%d",&n);
for(int i=;i<n;i++)
{
int a, b, v;
scanf("%d%d%d",&a,&b,&v);
r+=v;
addedge(a,b,v);
addedge(b,a,v);
}
dfs(,);
for(int i=head[];i;i=nex[i])
{
int p=to[i];
dfs1(p,,p,val[i]);
}
update();
scanf("%d",&m);
for(int i=;i<=m;i++)
scanf("%d",&army[i]);
int idx=;
for(int i=head[];i;i=nex[i])
if(to[i]!=)
idx++;
if(m<idx)
{
printf("-1");
return ;
}
l=;
int ans;
while(l<r)
{
int mid=(l+r)>>;
if(check(mid))
ans=mid,r=mid;
else
l=mid+;
}
printf("%d",ans);
return ;
}

  呃。。由于。。代码写的丑。。。在自己学校网站 (链接:https://neooj.com:8082/oldoj/)被卡了时间。。。。 于是有了下面的优化代码 (感谢@Yang1208

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define O3 __attribute__((optimize("-O3")))
const int N = ;
const int root=;
const int logn = ;
int head[N];
int to[N<<];
int nex[N<<];
int val[N<<];
int cnt,n,m;
int fa[N][logn+];
int top[N];
int tdis[N];
int army[N];
int dis[N];
bool pos[N];
int q[N];
bool vis[N];
int cnt_a;
O3 char nc() {
static char buf[],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,,,stdin),p1==p2)?EOF:*p1++;
}
O3 inline int rd() {
register int x=;char s=nc();
while(s<''||s>'')s=nc();
while(s>=''&&s<='')x=x*+s-'',s=nc();
return x;
}
O3 void addedge(int a, int b,int v)
{
nex[++cnt]=head[a];
head[a]=cnt;
to[cnt]=b;
val[cnt]=v;
}
O3 void dfs(int p,int f)
{
for(int i=head[p];i;i=nex[i])
{
if(to[i]!=f)
{
dis[to[i]]=dis[p]+val[i];
fa[to[i]][]=p;
dfs(to[i],p);
}
}
}
O3 void update()
{
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
}
O3 void dfs1(int p,int f,int topf,int dist)
{
top[p]=topf;
tdis[p]=dist;
bool ft=;
for(int i=head[p];i;i=nex[i])
{
int v=to[i];
if(v!=f)
{
ft=;
dfs1(v,p,topf,dist);
}
}
if(!ft)
pos[p]=;
}
bool fs;
O3 void dfs2(int p,int f)
{
if(pos[p])
{
fs=;
return ;
}
for(int i=head[p];i;i=nex[i])
{
int v=to[i];
if(v!=f&&!vis[v])
{
dfs2(v,p);
if(fs)
return ;
}
}
}
O3 inline bool check2(int p)
{
fs=;
dfs2(p,fa[p][]);
return fs;
}
struct node
{
int up;
int rest;
}stop[N];
O3 inline bool cmp1(node a,node b)
{
return a.rest<b.rest;
}
O3 inline bool cmp2(node a,node b)
{
return a.rest>b.rest;
}
O3 inline bool cmp(int a,int b)
{
return a>b;
}
O3 bool check(int time)
{
memset(stop,,sizeof(stop));
memset(vis,,sizeof(vis));
memset(q,,sizeof(q));
cnt_a=;
for(int i=;i<=m;i++)
{
int time2=time;
int now=army[i];
bool can=;
while()
{
for(int j=;j>=;j--)
{
if(fa[now][j]&&dis[now]-dis[fa[now][j]]<=time2)
{
time2-=dis[now]-dis[fa[now][j]];
now=fa[now][j];
break;
}
if(j==||now==)
{
can=;
break;
}
}
if(can)
break;
}
if(now==)
{
stop[++cnt_a].up=top[army[i]];
stop[cnt_a].rest=time2;
}
else
vis[now]=;
}
sort(stop+,stop+m+,cmp1);
for(int i=;i<=m;i++)
{
if(stop[i].rest<tdis[stop[i].up])
{
if(!vis[stop[i].up]&&check2(stop[i].up))
{
vis[stop[i].up]=;
stop[i].rest=-;
}
}
}
int tail=;
sort(stop+,stop+m+,cmp2);
for(int i=head[];i;i=nex[i])
{
int v=to[i];
if(!vis[v]&&check2(v))
q[++tail]=val[i];
}
sort(q+,q+tail+,cmp);
for(int i=;i<=tail;i++)
if(stop[i].rest<q[i])
return false;
return true;
}
O3 int main()
{
int l, r;
n=rd();
for(int i=;i<n;i++)
{
int a, b, v;
a=rd();b=rd();v=rd();
r+=v;
addedge(a,b,v);
addedge(b,a,v);
}
dfs(,);
for(int i=head[];i;i=nex[i])
{
int p=to[i];
dfs1(p,,p,val[i]);
}
update();m=rd();
for(int i=;i<=m;i++) army[i]=rd();
int idx=;
for(int i=head[];i;i=nex[i])
if(to[i]!=)
idx++;
if(m<idx)
{
printf("-1");
return ;
}
l=;
int ans;
while(l<r)
{
int mid=(l+r)>>;
if(check(mid)) r=mid;
else l=mid+;
}
printf("%d",l);
return ;
}

NOIP2012 D2 T3 疫情控制 洛谷P1084的更多相关文章

  1. [NOIP2012] day2 T3疫情控制

    题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到 ...

  2. [NOIP2012提高组]疫情控制

    题目:洛谷P1084.codevs1218.Vijos P1783. 题目大意:有一棵n个节点的,根为1的带权树和m支军队.每支军队可以在一个点上停下,那么从1开始就不能经过这个点了.现在有m支军队已 ...

  3. 洛谷P1084 疫情控制(NOIP2012)(二分答案,贪心,树形DP)

    洛谷题目传送门 费了几个小时杠掉此题,如果不是那水水的数据的话,跟列队的难度真的是有得一比... 话说蒟蒻仔细翻了所有的题解,发现巨佬写的都是倍增,复杂度是\(O(n\log n\log nw)\)的 ...

  4. 洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

    P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控 ...

  5. NOIP2012 Day1 T2国王游戏 洛谷P1080

    第一篇博客啊…… 由于我太弱了,还要去补不全的知识点准备参加人生第一次NOIp,所以第一篇博客就简短一点好了(偷懒就直说吧……) 洛谷P1080传送门 题意概括: 有N对数ai和bi,以及两个数a0和 ...

  6. [NOIP2012] 提高组 洛谷P1084 疫情控制

    题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散 ...

  7. 洛谷P1084 疫情控制 [noip2012] 贪心+树论+二分答案 (还有个小bugQAQ

    正解:贪心+倍增+二分答案 解题报告: 正好想做noip的题目然后又想落实学长之前讲的题?于是就找上了这题 其实之前做过,70,然后实在细节太多太复杂就不了了之,现在再看一遍感觉又一脸懵了... 从标 ...

  8. NOIP2012 洛谷P1084 疫情控制

    Description: H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情 ...

  9. 洛谷 P1084 疫情控制 —— 二分+码力

    题目:https://www.luogu.org/problemnew/show/P1084 5个月前曾经写过一次,某个上学日的深夜,精疲力竭后只有区区10分,从此没管... #include< ...

随机推荐

  1. java并发编程(二十六)----ThreadLocal的使用

    其实ThreadLocal很多接触过多线程的同学都可能会很陌生,他不像current包里面那些耳熟能详的api一样在我们面前经常出现,更多的他作为一个本地类出现在系统设计里面.我们可以说一下Sprin ...

  2. 神盘GCCX,2019必撸大毛!

    自从今年5月转型投资以来,已经很少薅羊毛了! 不是不撸,是因为一般的羊毛我真看不上! 撸羊毛能不能发财,能不能日入几百几千! 答案是,可以! 干羊毛,像趣步,云钱包,云比特,环保币,很多人都发财了!前 ...

  3. Visual Studio 2015&2017 key

    Visual Studio 2015 key Key : HMGNV-WCYXV-X7G9W-YCX63-B98R2 Visual Studio Enterprise 2015 Key :HM6NR- ...

  4. DFS树求割点问题

    时间复杂度:O(n玄学)总之不大 代码实现(好麻烦,蓝题变紫题) #include<iostream> #include<string.h> #include<algor ...

  5. Redux概览

    简介 Redux 是一个有用的架构 Redux 的适用场景:多交互.多数据源 工作流程图 action 用户请求 //发出一个action import { createStore } from 'r ...

  6. Linux系统@根目录下各目录作用归纳图

  7. CMS和G1的区别

    CMS:以获取最短回收停顿时间为目标的收集器,基于并发“标记清理”实现 过程: 1.初始标记:独占PUC,仅标记GCroots能直接关联的对象 2.并发标记:可以和用户线程并行执行,标记所有可达对象 ...

  8. 企查查app (完结)

    在经历前两次探索之后,终于可以把所有的加密关键参数搞定了. 已删除!!!! 好了现在基本结束了. 根据这个我写了一自动抓取企查查每日新增数据,需要的话可以去看看 企查查app新增企业数据抓取 关注小白 ...

  9. 怒改springMVC项目为springBoot项目

    背景:公司最近在做项目升级,融合所有项目,但是目前使用的一个系统还是最原始的框架 springMVC+spring+mybatis ,前端还是jsp,easyui(技术老的掉牙),终于出手了,结果.. ...

  10. Hugo

    快速开始 安装Hugo 1.二进制安装(推荐:简单.快速) 到 Hugo Releases 下载对应的操作系统版本的Hugo二进制文件(hugo或者hugo.exe) Mac下直接使用 ==Homeb ...