博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
P2680 运输计划(树上差分+lca+二分)
阅读量:4136 次
发布时间:2019-05-25

本文共 3396 字,大约阅读时间需要 11 分钟。

题目背景

公元 20442044 年,人类进入了宇宙纪元。

题目描述

公元20442044 年,人类进入了宇宙纪元。

L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条航道建立在两个星球之间,这 n-1n−1 条航道连通了 LL 国的所有星球。

小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要u_iu i号星球沿最快的宇航路径飞行到 v_iv i号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 jj,任意飞船驶过它所花费的时间为 t_jt j,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, LL 国国王同意小 PP 的物流公司参与 LL 国的航道建设,即允许小PP 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 mm 个运输计划。在虫洞建设完成后,这 mm 个运输计划会同时开始,所有飞船一起出发。当这 mm 个运输计划都完成时,小 PP 的物流公司的阶段性工作就完成了。

如果小 PP 可以自由选择将哪一条航道改造成虫洞, 试求出小 PP 的物流公司完成阶段性工作所需要的最短时间是多少?

输入格式

第一行包括两个正整数 n, mn,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 11 到 nn 编号。

接下来 n-1n−1 行描述航道的建设情况,其中第 ii 行包含三个整数 a_i, b_ia

i
​ ,b
i
​ 和 t_it
i
​ ,表示第 ii 条双向航道修建在 a_ia
i
​ 与 b_ib
i
​ 两个星球之间,任意飞船驶过它所花费的时间为 t_it
i
​ 。数据保证 1 \leq a_i,b_i \leq n1≤a
i
​ ,b
i
​ ≤n 且 0 \leq t_i \leq 10000≤t
i
​ ≤1000。

接下来 mm 行描述运输计划的情况,其中第 jj 行包含两个正整数 u_ju

j
​ 和 v_jv
j
​ ,表示第 jj 个运输计划是从 u_ju
j
​ 号星球飞往 v_jv
j
​ 号星球。数据保证 1 \leq u_i,v_i \leq n1≤u
i
​ ,v
i
​ ≤n

输出格式

一个整数,表示小 PP 的物流公司完成阶段性工作所需要的最短时间。

输入输出样例

输入 #1 复制
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
输出 #1 复制
11
说明/提示
所有测试数据的范围和特点如下表所示
在这里插入图片描述
求所有边最大值的最小化。这种题一看就是二分。
关于边的差分(如找被所有路径共同覆盖的边
首先我们除了一般的grand,depth等数组以外,多开两个数组:tmp和prev。

tmp用来记录点的出现次数(具体点说实际上记录的是点到其父亲的边的出现次数),prev记录每个点到其父亲的那条边。对于一条起点s,终点t的路径。我们这样处理:

tmp[s]++,tmp[t]++,tmp[LCA(s,t)]-=2。(记住:最后要从所有叶结点把权值向上累加。)以一次操作为例,我们来看看效果(可以画一张图)。首先tmp[s]++,一直推上去到根,这时候s到root的路径访问次数都+1,tmp[t]++后,t到lca路径加了1,s到lca路径加了1,而lca到根的路径加了2。

这时,我们只需要tmp[LCA(s,t)]-=2,推到根,就能把那些多余的路径减掉,达到想要的目的。而这是一次操作,对于很多次操作的话,我们只需要维护tmp,而不必每次更新到根,维护好tmp最后Dfs一遍即可。这时如果tmp[i]==次数的话,说明i到其父亲的边是被所有路径覆盖的。如图

在这里插入图片描述
代码如下:

#include
#define ll long longusing namespace std;const int maxx=3e5+100;struct node{
int x,y,lca;}b[maxx];struct edge{
int to,next,val;}e[maxx<<1];int head[maxx<<1],temp[maxx],deep[maxx],dp[maxx][26],pre[maxx];ll dis[maxx];int n,m,tot,flag;/*--------------事前准备----------------*/inline void add(int u,int v,int w){
e[tot].to=v,e[tot].next=head[u],e[tot].val=w,head[u]=tot++;}inline void init(){
for(int i=0;i<=n;i++) {
for(int j=0;j<=25;j++) dp[i][j]=0; } memset(head,-1,sizeof(head)); tot=0;}/*-----------dfs-----------*/inline void dfs(int u,int f){
deep[u]=deep[f]+1; dp[u][0]=f; for(int i=1;i<=25;i++) {
if(dp[u][i-1]) dp[u][i]=dp[dp[u][i-1]][i-1]; else break; } for(int i=head[u];i!=-1;i=e[i].next) {
int to=e[i].to,w=e[i].val; if(to==f) continue; dis[to]=dis[u]+w; pre[to]=w; dfs(to,u); }}inline int dfs2(int u,int f,int cnt,ll maxn){
int num=temp[u]; for(int i=head[u];i!=-1;i=e[i].next) {
int to=e[i].to; if(to==f) continue; num+=dfs2(to,u,cnt,maxn); } if(num>=cnt&&pre[u]>=maxn) flag=1; return num;}/*-----------lca------------*/inline int get_lca(int x,int y){
if(deep[x]
=0;i--) {
if(dp[x][i]!=dp[y][i]) {
x=dp[x][i]; y=dp[y][i]; } } return dp[x][0];}/*--------------二分-------------*/inline int check(ll x)//二分找出大于这个值的边的条数和最大差值{
ll sum;int cnt=0;ll maxn=0; memset(temp,0,sizeof(temp)); for(int i=1;i<=m;i++) {
sum=dis[b[i].x]+dis[b[i].y]-2*dis[b[i].lca]; if(sum>x) {
cnt++; temp[b[i].x]++,temp[b[i].y]++,temp[b[i].lca]-=2; maxn=max(maxn,sum-x); } } if(cnt==0) return 1; flag=0; dfs2(1,0,cnt,maxn); return flag;}int main(){
int x,y,w;ll sum,mid; scanf("%d%d",&n,&m); sum=0; init(); for(int i=1;i
>1; if(check(mid)) {
ans=mid; r=mid-1; } else l=mid+1; } printf("%lld\n",ans); return 0;}

努力加油a啊,(o)/~

转载地址:http://mztvi.baihongyu.com/

你可能感兴趣的文章
经典shell面试题整理
查看>>
腾讯的一道面试题—不用除法求数字乘积
查看>>
素数算法
查看>>
java多线程环境单例模式实现详解
查看>>
将一个数插入到有序的数列中,插入后的数列仍然有序
查看>>
在有序的数列中查找某数,若该数在此数列中,则输出它所在的位置,否则输出no found
查看>>
万年历
查看>>
作为码农你希望面试官当场指出你错误么?有面试官这样遭到投诉!
查看>>
好多程序员都认为写ppt是很虚的技能,可事实真的是这样么?
查看>>
如果按照代码行数发薪水会怎样?码农:我能刷到公司破产!
查看>>
程序员失误造成服务停用3小时,只得到半月辞退补偿,发帖喊冤
查看>>
码农:很多人称我“技术”,感觉这是不尊重!纠正无果后果断辞职
查看>>
php程序员看过来,这老外是在吐糟你吗?看看你中了几点!
查看>>
为什么说程序员是“培训班出来的”就是鄙视呢?
查看>>
码农吐糟同事:写代码低调点不行么?空格回车键与你有仇吗?
查看>>
阿里p8程序员四年提交6000次代码的确有功,但一次错误让人唏嘘!
查看>>
一道技术问题引起的遐想,最后得出结论技术的本质是多么的朴实!
查看>>
985硕士:非科班自学编程感觉还不如培训班出来的,硕士白读了?
查看>>
你准备写代码到多少岁?程序员们是这么回答的!
查看>>
码农:和产品对一天需求,产品经理的需求是对完了,可我代码呢?
查看>>