题目链接: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. React Native 混合开发与实现

    关于 微信公众号:前端呼啦圈(Love-FED) 我的博客:劳卜的博客 知乎专栏:前端呼啦圈 前言 随着 React 的盛行,其移动开发框架 React Native 也收到了广大开发者的青睐,以下简 ...

  2. 大数据学习之旅2——从零开始搭hadoop完全分布式集群

    前言 本文从零开始搭hadoop完全分布式集群,大概花费了一天的时间边搭边写博客,一步一步完成完成集群配置,所以相信大家按照本文一步一步来完全可以搭建成功.需要注意的是本文限于篇幅和时间的限制,也是为 ...

  3. ASP.NET CORE 2.* 利用集成测试框架覆盖HttpClient相关代码

    ASP.NET CORE 集成测试官方介绍 我的asp.net core 项目里面大部分功能都是去调用别人的API ,大量使用HttpClient,公司单元测试覆盖率要求95%以上,很难做到不mock ...

  4. Visual Studio 2015&2017 key

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

  5. 关于sparksql中设置自定义自增列的相关要点(工作共踩过的坑-1)

    小白终于进入了职场,从事大数据方面的工作! 分到项目组了,搬砖的时候遇到了一个这样的问题. 要求:用spark实现oracle的存储过程中计算部分. 坑:由于报表中包含了一个ID字段,其要求是不同的区 ...

  6. app发布当天,用户无法登录

    原因:当用户登录时候有商城用户的触发器存在,它会让商城用户也更新成登录状态. 由于用户量大,导致数据库锁死. 最后解决案:删掉触发器,在app的接口登录程序里,追加商城用户更新成登录的操作. 他案1: ...

  7. python学习之并发编程

    目录 一.并发编程之多进程 1.multiprocessing模块介绍 2.Process类的介绍 3.Process类的使用 3.1 创建开启子进程的两种方式 3.2 获取进程pid 3.3验证进程 ...

  8. Condition控制线程通信

    Condition控制线程通信 一.前言 java.util.concurrent.locks.Condition 接口描述了可能会与锁有关联的条件变量.这些变量在用法上与使用Object.wait ...

  9. 使用WPF为Powershell程序制作GUI界面

    1. 使用Xaml创建应用界面 打开visual studio,创建一个新的项目,在已安装模板中选择Visual C# →Wpf应用. 完成创建后,我们得到如下图所示的应用界面. wpf界面是基于xa ...

  10. HTML连载34-背景关联和缩写以及插图图片和背景图片的区别

    一.背景属性缩写的格式 1.backgound:背景颜色  背景图片  平铺方式  关联方式  定位方式 2.注意点: 这里的所有值都可以省略,但是至少需要一个 3.什么是背景关联方式 默认情况下,背 ...