NOIP2012 D2 T3 疫情控制 洛谷P1084
题目链接: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的更多相关文章
- [NOIP2012] day2 T3疫情控制
题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到 ...
- [NOIP2012提高组]疫情控制
题目:洛谷P1084.codevs1218.Vijos P1783. 题目大意:有一棵n个节点的,根为1的带权树和m支军队.每支军队可以在一个点上停下,那么从1开始就不能经过这个点了.现在有m支军队已 ...
- 洛谷P1084 疫情控制(NOIP2012)(二分答案,贪心,树形DP)
洛谷题目传送门 费了几个小时杠掉此题,如果不是那水水的数据的话,跟列队的难度真的是有得一比... 话说蒟蒻仔细翻了所有的题解,发现巨佬写的都是倍增,复杂度是\(O(n\log n\log nw)\)的 ...
- 洛谷P1084 [NOIP2012提高组Day2T3]疫情控制
P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控 ...
- NOIP2012 Day1 T2国王游戏 洛谷P1080
第一篇博客啊…… 由于我太弱了,还要去补不全的知识点准备参加人生第一次NOIp,所以第一篇博客就简短一点好了(偷懒就直说吧……) 洛谷P1080传送门 题意概括: 有N对数ai和bi,以及两个数a0和 ...
- [NOIP2012] 提高组 洛谷P1084 疫情控制
题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散 ...
- 洛谷P1084 疫情控制 [noip2012] 贪心+树论+二分答案 (还有个小bugQAQ
正解:贪心+倍增+二分答案 解题报告: 正好想做noip的题目然后又想落实学长之前讲的题?于是就找上了这题 其实之前做过,70,然后实在细节太多太复杂就不了了之,现在再看一遍感觉又一脸懵了... 从标 ...
- NOIP2012 洛谷P1084 疫情控制
Description: H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情 ...
- 洛谷 P1084 疫情控制 —— 二分+码力
题目:https://www.luogu.org/problemnew/show/P1084 5个月前曾经写过一次,某个上学日的深夜,精疲力竭后只有区区10分,从此没管... #include< ...
随机推荐
- Go中的日志及第三方日志包logrus
有别的语言使用基础的同学工作中都会接触到日志的使用,Go中自然也有log相关的实现.Go log模块主要提供了3类接口,分别是 "Print .Panic .Fatal ",对每一 ...
- QT动画时间轴控制 QTimeLine
QTimeLine类提供用于控制动画的时间轴 比如控制进度条的增长,图片,窗口的旋转,平移等等 QTimeLine有一个frameChanged(int)信号 当调用QTimeLine::start( ...
- SpringBoot中Shiro缓存使用Redis、Ehcache
在SpringBoot中Shiro缓存使用Redis.Ehcache实现的两种方式实例 SpringBoot 中配置redis作为session 缓存器. 让shiro引用 本文是建立在你是使用这sh ...
- 简单了解一下事件循环(Event Loop)
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- AutoResetEvent控制线程用法
本文主要来自一道面试题,由于之前对AutoResetEvent的用户很模糊(即使已经使用过了).面试题题目很简洁:两个线程交替打印0~100的奇偶数.你可以先动手试试,我主要是尝试在一个方法里面完成这 ...
- hbase集群region数量和大小的影响
1.Region数量的影响 通常较少的region数量可使群集运行的更加平稳,官方指出每个RegionServer大约100个regions的时候效果最好,理由如下: 1)Hbase的一个特性MSLA ...
- WPF DataGrid点击列头选择全列并具有背景色
完成这个操作,主要是XAML的代码. 主要思路是通过绑定多路数据,在多路转换器中返回布尔值,在通过数据触发器来设置被选择的全列的背景色. XAML页面主要代码 首先定义DataGridCell < ...
- Java网络编程与NIO详解8:浅析mmap和Direct Buffer
微信公众号[黄小斜]作者是蚂蚁金服 JAVA 工程师,目前在蚂蚁财富负责后端开发工作,专注于 JAVA 后端技术栈,同时也懂点投资理财,坚持学习和写作,用大厂程序员的视角解读技术与互联网,我的世界里不 ...
- vue入门:用户管理demo1
该demo由前端请求后台服务器获取数据进行渲染 使用到的技术点 1.使用到的vue指令:{{}} v-if v-for v-model 2.使用到的事件:@click 点击事件, @keyup.ent ...
- 前端传递给后端复杂类型 webapi 等
来自URL:https://www.cnblogs.com/kylewell/p/5674564.html 前端我需要通过ajax 传递如下的一个json对象: var model = { Param ...