题目链接: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. 【0801 | Day 6】Python基础(四)

    Part 13 流程控制之while循环 一.语法 while 条件 code 1 code 2 code 3 ... ​ while True: print('*1'*100) print('*2' ...

  2. Python 命令行之旅 —— 初探 argparse

    『讲解开源项目系列』启动--让对开源项目感兴趣的人不再畏惧.让开源项目的发起者不再孤单.跟着我们的文章,你会发现编程的乐趣.使用和发现参与开源项目如此简单.欢迎联系我们给我们投稿,让更多人爱上开源.贡 ...

  3. 基于jmeter+perfmon的稳定性测试记录

    1. 引子 最近承接了项目中一些性能测试的任务,因此决定记录一下,将测试的过程和一些心得收录下来. 说起来性能测试算是软件测试行业内,有些特殊的部分.这部分的测试活动,与传统的测试任务差别是比较大的, ...

  4. 201412-2 Z字形扫描(c语言)

    问题描述 在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan).给定一个n×n的矩阵,Z字形扫描的过程如下图所示: 对于下面的4×4的矩阵, 1 5 3 9 3 7 5 ...

  5. ABAP_增强点查找

    *&---------------------------------------------------------------------* *& Report Z_FIND_EN ...

  6. SAP Special Fields in FAGLL03 transaction

    https://wiki.scn.sap.com/wiki/display/ERPFI/Special+Fields+in+FAGLL03+transaction https://wiki.scn.s ...

  7. SAP-批量删除生产订单

    1.SE38运行:PPARCHP1 2.先用COOIS导出订单,已经CLSD,没有删除的

  8. Keras载入mnist数据集出错问题解决方案

    找到本地keras目录下的mnist.py文件 通常在这个目录下. ..\Anaconda3\Lib\site-packages\keras\datasets 下载mnist.npz文件到本地 下载链 ...

  9. Django Mysql数据库-F查询和Q查询

    一.F查询和Q查询 F查询: 在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较.如果我们要对两个字段的值做比较,那该怎么做呢? Django 提供 F() 来做这样的比较.F() 的 ...

  10. ASP.NET Core Web API

    1.简单介绍 ASP.NET Core Web API 是 ASP.NET Core MVC 的一个功能.ASP.NET Core MVC 包含了对 Web API 的支持.可以构建多种客户端的 HT ...