传送门

解题思路

  比较神的一道题。首先发现是最小值问题,并且具有单调性,所以要考虑二分答案。其次有一个性质是军队越靠上越优,所以我们要将所有的军队尽量向上提,这一过程我们用倍增实现。发现这时有两种军队,一种是到根之后可以继续移动,另一种不行。那么记录下来那些到根之后可以移动的点和可以继续移动的距离,存到一个结构体里,顺便还要记录下来每个根节点的子节点中剩余路程最短的点。继续一遍\(dfs\),可以求出那些需要被占领的根节点的子节点,然后记录他们到根的距离。把两个数组从大到小排序,贪心的选取剩余路程大于需要占领的子节点到根的距离的那些点去占领,注意也要考虑到不移动的情况。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib> using namespace std;
const int MAXN = 50005;
typedef long long LL; inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
} int n,m,head[MAXN],cnt,to[MAXN<<1],nxt[MAXN<<1],val[MAXN<<1],a[MAXN];
int f[MAXN][23],ar[MAXN],resid[MAXN],cntb,cnta;
LL dis[MAXN][23],ans,sum,resmin[MAXN];
bool vis[MAXN],use[MAXN]; struct Data{
int id;LL res;
friend bool operator<(const Data A,const Data B){
return A.res>B.res;
}
}tmp[MAXN],b[MAXN]; inline void add(int bg,int ed,int w){
to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=w,head[bg]=cnt;
} void dfs(int x,int fa,int w){
f[x][0]=fa;dis[x][0]=w;int u;
for(int i=1;i<=20;i++){
f[x][i]=f[f[x][i-1]][i-1];
dis[x][i]=dis[x][i-1]+dis[f[x][i-1]][i-1];
// printf("f[%d][%d]=%d\n",x,i,f[x][i]);
}
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==fa) continue;
dfs(u,x,val[i]);
}
} bool dfs2(int x,int fa){
int u;bool flag=false,ok=true;if(vis[x]) return 1;
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==fa) continue;flag=1;
if(!dfs2(u,x)){ok=false;
if(x==1) b[++cntb].id=u,b[cntb].res=val[i];
else return false;
}
}
if(!flag) return false;
return ok;
} inline bool check(LL lim){
cnta=0,cntb=0;int L=1;
memset(use,0,sizeof(use));
memset(vis,0,sizeof(vis));
memset(resmin,0,sizeof(resmin));
memset(resid,0,sizeof(resid));
for(int i=1;i<=m;i++){
ar[i]=a[i];LL now=lim;
for(int j=20;j>=0;j--)
if(f[ar[i]][j]>1 && now>=dis[ar[i]][j]) now-=dis[ar[i]][j],ar[i]=f[ar[i]][j];
if(f[ar[i]][0]==1 && now-dis[ar[i]][0]>0) {
tmp[++cnta].res=now-dis[ar[i]][0];tmp[cnta].id=i;
if(tmp[cnta].res<resmin[ar[i]] || !resmin[ar[i]])
resmin[ar[i]]=tmp[cnta].res,resid[ar[i]]=i;
}
else vis[ar[i]]=1;
}
dfs2(1,0);if(!cntb) return true;
sort(tmp+1,tmp+1+cnta);sort(b+1,b+1+cntb);use[0]=1;
for(int i=1;i<=cntb;i++){
if(!use[resid[b[i].id]]) {use[resid[b[i].id]]=1;continue;}
while(L<=cnta && (tmp[L].res<b[i].res || use[tmp[L].id])) L++;
if(L>cnta) return false;use[tmp[L].id]=1;L++;
}
return true;
} int main(){
// freopen("1.in","r",stdin);
n=rd();int x,y,z;
for(int i=1;i<n;i++){
x=rd(),y=rd(),z=rd();
add(x,y,z),add(y,x,z);sum+=z;
}
m=rd();for(int i=1;i<=m;i++) a[i]=rd();
dfs(1,0,0);LL l=0,r=sum,mid;
while(l<=r) {
mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}
/*
10
2 1 3
2 3 4
1 4 7
5 1 9
6 1 2
4 7 9
7 8 8
9 8 8
1 10 2
5
2 8 5 4 2
*/

LUOGU P1084 疫情控制(二分+贪心+树上倍增)的更多相关文章

  1. Luogu P1084 疫情控制 | 二分答案 贪心

    题目链接 观察题目,答案明显具有单调性. 因为如果用$x$小时能够控制疫情,那么用$(x+1)$小时也一定能控制疫情. 由此想到二分答案,将问题转换为判断用$x$小时是否能控制疫情. 对于那些在$x$ ...

  2. luogu P1084疫情控制 二分

    链接 loj luogu太水不要去了. 思路 二分. 每个军队在一定的时间内越往上越好. 注意一个军队可以跨过1去帮别的. 把能到1脚下的点都存下来特判. 有一种情况是这个子树内只有一个军队,但这个军 ...

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

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

  4. 洛谷P1084 疫情控制(贪心+倍增)

    这个题以前写过一遍,现在再来写,感觉以前感觉特别不好写的细节现在好些多了,还是有进步吧. 这个题的核心思想就是贪心+二分.因为要求最小时间,直接来求问题将会变得十分麻烦,但是如果转换为二分答案来判断可 ...

  5. NOIP2012疫情控制(二分答案+树上贪心)

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

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

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

  7. luogu P1084 疫情控制

    传送门 首先,所有军队又要尽量往上走,这样才能尽可能的封锁更多的到叶子的路径 而随着时间的增加,能封锁的路径也就越来越多,所以可以二分最终的时间 然后对于每个时间,就让能走到根的军队走到根,记录到根上 ...

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

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

  9. 洛谷P1084 疫情控制

    题目 细节比较多的二分+跟LCA倍增差不多的思想 首先有这样一个贪心思路,深度越低的检查点越好,而最长时间和深度具有单调性,即给定时间越长,每个军队能向更浅的地方放置检查点.因此可以考虑二分时间,然后 ...

随机推荐

  1. Vue之自建管理后台(三)登录页面

    在做登录页面之前,我们必须得完成路由的设定... 按照之前的设计我们路由的文件夹是src/router 官方默认的index.js,如下: import Vue from 'vue' import R ...

  2. 使用jQuery的datetimepicker插件

    因为后台系统要使用年月日时分的设置,又因为使用的Bootstrap框架只有datepicker和timepicker控件.所以在jQuery库中找到轻量级的datetimepicker插件,很好地解决 ...

  3. 笔记55 Mybatis快速入门(六)

    相关概念介绍(一) 1.日志 有时候需要打印日志,知道mybatis执行了什么样的SQL语句,以便进行调试.这时,就需要开启日志,而mybatis自身是没有带日志的,使用的都是第三方日志,这里介绍如何 ...

  4. ubuntu配置阿里云源

    换成国内最快的阿里云源 第一步:备份原来的源文件 cd /etc/apt/ 然后会显示下面的源文件sources.list 输入命令 sudo cp sources.list sources.list ...

  5. tomcat 端口被占用 项目端口号被占用怎么解决

    1.Win+R  打开运行 ,输入cmd 打开命令行窗口 . 2.假设要查询端口被占用情况,在命令行下输入:netstat  -aon|findstr  "8883" 3.得到进程 ...

  6. 43. 守护线程 和 join方法

    1.守护线程(后台线程):            我们在使用一款软件的时候,有的软件会让我们在不知道的情况下下载一些东西,那么这个就是后台线程.            一般用于提高软件的下载量(也就是 ...

  7. JUC 一 CountDownLatch(闭锁)

    java.util.concurrent 介绍 CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待 CountDownLatch cou ...

  8. 依赖背包变形(经典)——poj1155

    这个题用优化后的依赖背包做难以实现,所以用常规的泛化物品的和来做即可 每个节点的容量定义为这个节点下的叶子结点个数,dp[u][j]用来表示节点u下选取j个物品的最大收益,最后从m-0查询dp[1][ ...

  9. 2019年12月12日英语学习-Will I Or Won't I ?

    这节英语课上的内容没记住多少东西,觉得这个主题太枯燥了,不过整堂课和外教沟通交流还是不错的,因为这节课就我一个学生.给我了充分的机会去张嘴交流互动. 也没记住什么东西,不知道写什么.只记住将要决定做某 ...

  10. java排序算法之冒泡排序和快速排序

    总结一下Java排序算法,以便记忆. 各类排序的时间复杂度: 排序方法 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性 复杂性 直接插入排序 O(n2)O(n2) O( ...