【BZOJ2282】[Sdoi2011]消防 树形DP+双指针法+单调队列
2023-09-11 14:15:24 时间
【BZOJ2282】[Sdoi2011]消防
Description
某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。
这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。
现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。
你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。
Input
输入包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
Output
输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。
Sample Input
【样例输入1】
5 2
1 2 5
2 3 2
2 4 4
2 5 3
【样例输入2】
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
5 2
1 2 5
2 3 2
2 4 4
2 5 3
【样例输入2】
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
Sample Output
【样例输出1】
5
【样例输出2】
5
5
【样例输出2】
5
HINT
对于100%的数据,n<=300000,边长小等于1000。
题解:首先,选出的路径一定在某条直径上(可以用反证法证明)。
然后我们将直径拎出来,假设我们最后选定的是直径上的[l,r]这部分,那么距离的最大值就是:
max(直径的左端点到l的距离,直径的右端点到r的距离,[l,r]中每个节点的子树(不包含直径的部分)中到该节点最远的距离)
前两个很好求,第三个怎么办?可以先预处理出每个点的子树中最远的距离,然后用单调队列查询最大值。然后就可以用双指针法来确定[l,r]了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn=300010; int n,m,cnt,len,r1,r2,h,t,ans; int to[maxn<<1],next[maxn<<1],head[maxn],val[maxn<<1],fa[maxn],p[maxn],vis[maxn],f[maxn],dep[maxn],q[maxn],v[maxn]; void dfs(int x) { if(dep[x]>dep[r2]) r2=x; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]) fa[to[i]]=x,dep[to[i]]=dep[x]+val[i],dfs(to[i]); } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } int getv(int x) { int tmp=dep[x]; for(int i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]&&to[i]!=fa[x]) tmp=max(tmp,getv(to[i])); return tmp; } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,j,a,b,c; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); r2=1,dfs(1); memset(dep,0,sizeof(dep)),memset(fa,0,sizeof(fa)); r1=r2,dfs(r1); for(a=r2;a;p[++len]=a,vis[a]=1,a=fa[a]); for(i=1;i<=len;i++) v[i]=getv(p[i])-dep[p[i]]; h=1,t=0,ans=1<<30; for(i=j=1;i<=len;i++) { for(;j<i&&dep[p[j]]-dep[p[i]]>m;j++); while(h<=t&&v[q[t]]<=v[i]) t--; q[++t]=i; while(h<=t&&q[h]<j) h++; ans=min(ans,max(max(dep[p[i]]-dep[p[len]],dep[p[1]]-dep[p[j]]),v[q[h]])); } printf("%d",ans); return 0; }
相关文章
- 数据结构系列文章之队列 FIFO
- freertos知识点笔记——队列、二值信号量、计数信号量
- 堆——神奇的优先队列(上)
- 一篇文全面读懂缓存与队列
- 阻塞队列,有界队列,无界队列
- 支军队正在进行阅兵前的训,训陈前队列排队是一个难题。该队列是一个n*n的方阵,排队要求是后一排的最低的不比前一排最高的低,同时要求偶数行从小到大排列,奇数行从大到小排列(行数从第0行开始,O为偶数)。输λn及η*n个身高数据〈身高数据为整型),按要求处理后输岀 n队列身高数据(每个身高数据占4个字符宽度)。
- 【C++】deque双端队列
- 系统可扩展设计方法之消息队列
- 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
- 《程序设计解题策略》——1.6 利用左偏树实现优先队列的合并
- 看动画学算法之:双向队列dequeue
- 【数据结构】链队列的C语言实现
- 详解JavaScript的任务、微任务、队列以及代码执行顺序
- OkHttp3源码详解(六) Okhttp任务队列工作原理
- hdu5242 上海邀请赛 优先队列+贪心
- (17)python消息队列,python RabbitMQ
- 关于Unity中LOD和渲染队列----渲染通道通用指令(一)
- JMS与消息队列