题目链接: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. IDEA+maven搭建scala开发环境(spark)(半转载)

    以下内容部分来自于https://zhuanlan.zhihu.com/p/23141509,我尝试了一遍,然后添加了一些图片.. 其实我觉得在IDEA中使用scala插件然后创建project的时候 ...

  2. 『开发技术』GPU训练加速原理(附KerasGPU训练技巧)

    0.深入理解GPU训练加速原理 我们都知道用GPU可以加速神经神经网络训练(相较于CPU),具体的速度对比可以参看我之前写的速度对比博文: [深度应用]·主流深度学习硬件速度对比(CPU,GPU,TP ...

  3. 8.7 day28 网络编程 socket套接字 半连接池 通信循环 粘包问题 struct模块

    前置知识:不同计算机程序之间的数据传输 应用程序中的数据都是从程序所在计算机内存中读取的. 内存中的数据是从硬盘读取或者网络传输过来的 不同计算机程序数据传输需要经过七层协议物理连接介质才能到达目标程 ...

  4. .NET中使用WebService,以及和一般处理程序、类库的区别

    首先我们来看一下如何创建Web Service 首先在解决方案中新建项,选择ASP.NETWeb应用程序 然后选择一个空的项目就可以,单击确定 项目建完之后,在项目上右键-->添加-->新 ...

  5. QFramework 使用指南 2020(二):下载与版本介绍

    目前 QFramework 有两个可供安装的版本 PackageKit:QFramework 的插件平台,可以下载只感兴趣的插件,除了 Framework 模块还有一些 Shader 案例.项目模板. ...

  6. 敏捷开发--必备工具Jira&Confluence学习视频

    敏捷开发必备工具:Jira+confluence,完美组合. 入门培训视频,内含Jira, Confluence, BigGantt, Zephyr, Tempo, Question, ScriptR ...

  7. Hive 系列(五)—— Hive 分区表和分桶表

    一.分区表 1.1 概念 Hive 中的表对应为 HDFS 上的指定目录,在查询数据时候,默认会对全表进行扫描,这样时间和性能的消耗都非常大. 分区为 HDFS 上表目录的子目录,数据按照分区存储在子 ...

  8. [ZJOI2011]看电影(组合数学,高精度)

    [ZJOI2011]看电影 这题模型转化很巧妙.(神仙题) 对于这种题首先肯定知道答案就是合法方案除以总方案. 总方案显然是\(k^n\). 那么考虑怎么算合法方案. 当\(n>k\)的时候显然 ...

  9. FILEBEAT+ELK日志收集平台搭建流程

    filebeat+elk日志收集平台搭建流程 1.         整体简介: 模式:单机 平台:Linux - centos - 7 ELK:elasticsearch.logstash.kiban ...

  10. MySQL里的COUNT

    count(*).count(1).count(主键).count(字段)的执行效率 在没有where条件的情况下 MyISAM引擎返回结果会比InnoDB快上很多,主要是因为MyISAM会单独记录了 ...