UOJ #150 【NOIP2015】 运输计划

题目描述

公元 \(2044\) 年,人类进入了宇宙纪元。

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

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

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

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

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

输入格式

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

接下来 $n-1$ 行描述航道的建设情况,其中第 $i$ 行包含三个整数 $a_i,b_i$ 和 $t_i$,表示第 $i$ 条双向航道修建在 $a_i$ 与 $b_i$ 两个星球之间,任意飞船驶过它所花费的时间为 $t_i$。数据保证 $1 \leq a_i,b_i \leq n$且 $0 \leq t_i \leq 1000$。

接下来 $m$ 行描述运输计划的情况,其中第 $j$ 行包含两个正整数 $u_j$ 和$v_j$,表示第 $j$ 个运输计划是从 $u_j$ 号星球飞往 $v_j$号星球。数据保证 $1 \leq u_i,v_i \leq n$

输出格式

输出文件只包含一个整数,表示小 $P$ 的物流公司完成阶段性工作所需要的最短时间。

 

  还记得当初在$noip$考场上,不会树剖不会二分答案,于是对于这道题就是狂跑lca啊lca……

  后来学了各种东西之后,就自己打了一个复杂度为$O(n \log ^2 n)$的算法,大意如下:

  先对于整棵树进行树链剖分,然后考虑二分一个答案(因为题目所求是最大值最小,所以答案单调),只需判断这个答案是否可行。于是我们需要把长度大于$x$的路径扫一遍,求一下这些路径的交,从交中找出一条权值最大的边,把这条边权值变为$0$(显然这样最优而且并不需要真的赋值为$0$),判断一下最长路径现在是否小于等于$x$即可。

  接着,发现被卡了。在UOJ上只有97分。于是,接下来就是奇技淫巧时间。

  有一次有点无聊,于是把树链剖分中的求重儿子部分的代码中的小于号改为了小于等于号,就这么AC了……汗……

  下面是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 2147483647
#define maxn 300001using namespace std;struct data{int f,t,v;
}he[maxn];
int fa[maxn],head[maxn*2],to[maxn*2],next[maxn*2],c[maxn*2];
int dep[maxn],son[maxn],top[maxn],siz[maxn],w[maxn],zui;
int tc[maxn],fc1[maxn],fc[maxn],cha[maxn+1],tt,m,n,x,y,l,r;int getint(){int w=0,q=0;char c=getchar();while((c>'9'||c<'0')&&c!='-') c=getchar();if(c=='-') q=1,c=getchar();while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();return q?-w:w; 
}void dfs1(int u,int dd){dep[u]=dd;siz[u]++;for(int i=head[u];i;i=next[i])if(!dep[to[i]]){dfs1(to[i],dd+1);siz[u]+=siz[to[i]];fa[to[i]]=u;fc[to[i]]=c[i];if(siz[to[i]]>=siz[son[u]]) son[u]=to[i];}
}void dfs2(int u,int dd){w[u]=++tt;top[u]=dd;fc1[tt]=fc[u];if(son[u]) tc[son[u]]=tc[u]+fc[son[u]],dfs2(son[u],dd);for(int i=head[u];i;i=next[i])if(to[i]!=son[u]&&to[i]!=fa[u])dfs2(to[i],to[i]);
}inline int lca(int x,int y){int ju=0;while(top[x]!=top[y]){int a=fa[top[x]],b=fa[top[y]];if(dep[a]<dep[b]) swap(x,y),swap(a,b);cha[w[x]+1]--;cha[w[top[x]]]++;ju+=tc[x]+fc[top[x]];x=a;}if(dep[x]<dep[y]) swap(x,y);cha[w[x]+1]--;cha[w[son[y]]]++;ju+=tc[x]-tc[y];return ju;
}inline bool pd(int kk){int ci=0;memset(cha,0,sizeof(cha));for(register int i=1;i<=m;i++)if(he[i].v>kk) lca(he[i].f,he[i].t),ci++;int hehe=0,hh=c[0];for(register int i=1;i<=tt;i++){hh+=cha[i];if(hh==ci) hehe=max(hehe,fc1[i]);}if(zui-hehe<=kk) return 1;return 0;
}int main(){n=getint();m=getint();for(int i=1;i<n;i++){x=getint();y=getint();to[++tt]=y,next[tt]=head[x],head[x]=tt;to[++tt]=x,next[tt]=head[y],head[y]=tt;c[tt-1]=c[tt]=getint();}dfs1(1,1);tt=0;dfs2(1,1);for(int i=1;i<=m;i++){he[i].f=getint();he[i].t=getint();he[i].v=lca(he[i].f,he[i].t);r=max(r,he[i].v);}zui=r++;while(l!=r){int mid=(l+r)>>1;if(pd(mid))r=mid;else l=mid+1;}printf("%d",l);return 0;
}

  但是后来,我发现其实这个算法是可以优化到$O(n \log n)$的。设当前二分的答案为$x$,由于每一次最长路径长度小于等于$x$时显然可以,否则不满足的所有路径的交必定在最长路径上(交为空时其实也是最长路径的子集),而且是连续的一段。然后我们把最长链抠出来,对于其他每一条路径与最长路径求一下交,那么路径的交就变成区间上的问题了,可以线性地做。于是复杂度降为$O(n \log n)$。

  还有怎么两条路径求交的问题。由于我们只需求出两条路径交的两个端点,可以把这两条路径的共4个点lca一下,分类讨论即可。

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 300010using namespace std;
typedef long long llg;struct data{int u,v,g,x;
}s[maxn];
int n,m,dep[maxn],son[maxn],siz[maxn],fa[maxn],tc[maxn],top[maxn],fc[maxn];
int head[maxn],to[maxn<<1],next[maxn<<1],c[maxn<<1],tt,K,nn,now;
int dc[maxn],cd[maxn],ld,b[maxn],lb,bb[maxn],ca[maxn];int getint(){int w=0;bool q=0;char c=getchar();while((c>'9'||c<'0')&&c!='-') c=getchar();if(c=='-') c=getchar(),q=1;while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();return q?-w:w;
}void link(int x,int y){to[++tt]=y;next[tt]=head[x];head[x]=tt;to[++tt]=x,next[tt]=head[y];head[y]=tt;c[tt]=c[tt-1]=getint();
}void dfs(int u){siz[u]=1;for(int i=head[u],v;v=to[i],i;i=next[i])if(!siz[v]){dep[v]=dep[u]+1; fa[v]=u; fc[v]=c[i];dfs(v); siz[u]+=siz[v];if(siz[v]>siz[son[u]]) son[u]=v;}
}void dfs(int u,int d){top[u]=d;if(son[u]) tc[son[u]]=tc[u]+fc[son[u]],dfs(son[u],d);for(int i=head[u],v;v=to[i],i;i=next[i])if(!top[v]) dfs(v,v);
}inline int lca(int u,int v){//树链剖分求lcanow=0;while(top[u]!=top[v]){if(dep[fa[top[u]]]<dep[fa[top[v]]]) swap(u,v);now+=tc[u]+fc[top[u]]; u=fa[top[u]];}if(dep[u]>dep[v]) swap(u,v);now+=tc[v]-tc[u]; return u;
}void kou(int u,int v,int g){//把最长路径抠出来while(u!=g) cd[++ld]=u,ca[ld]=fc[u],u=fa[u]; cd[++ld]=g; //从u到g的路径while(v!=g) b[++lb]=v,bb[lb]=fc[v],v=fa[v]; //从v到g的路径while(lb) ca[ld]=fc[b[lb]],cd[++ld]=b[lb--]; //ca表示长度for(int i=1;i<=ld;i++) dc[cd[i]]=i;
}inline bool pd(int x){//判断是否可行int l=1,r=ld,gi=0;for(register int i=1;i<=m;i++)if(s[i].x>x){l=max(l,s[i].u);r=min(r,s[i].v);}for(int i=l;i<r;i++) gi=max(gi,ca[i]);return nn-gi<=x;
}inline void gi(int &xx,int u){int x,y;x=lca(u,s[K].u); if(dep[x]>dep[s[K].g]){xx=dc[x];return;}y=lca(u,s[K].v); if(dep[y]>dep[s[K].g]){xx=dc[y];return;}xx=dc[s[K].g];//点在路径之外,要么另一个点的两个lca在最长路径上,此时视为最长路径的最上点,要么两条路径交为空
}int main(){n=getint(); m=getint();for(int i=1;i<n;i++) link(getint(),getint());dep[1]=1;dfs(1); dfs(1,1);for(register int i=1;i<=m;i++){s[i].u=getint(); s[i].v=getint();s[i].g=lca(s[i].u,s[i].v); s[i].x=now;if(now>nn) nn=now,K=i;//nn为最长路径长度,K为最长路径标号}kou(s[K].u,s[K].v,s[K].g);for(register int i=1,l,r;i<=m;i++)if(i!=K){gi(l,s[i].u); gi(r,s[i].v);//lca分类讨论求路径交if(l>r) swap(l,r);s[i].u=l,s[i].v=r;//注意这里的区间为[l,r)}s[K].u=1,s[K].v=ld;int l=0,r=nn,mid;while(l!=r){mid=l+r>>1;if(pd(mid)) r=mid;else l=mid+1;}printf("%d",l);
}

  又或者可以直接优化查分,使得差分变为$O(1)$,具体思路如下:把一条路径上的边全部加$1$,那么可以把两个端点的值加$1$,lca的值减$2$,全部完成后dfs一遍解决。但不知道为什么,速度比第一种算法还要慢,大概是常数有点大吧……

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 300010using namespace std;
typedef long long llg;struct data{int u,v,g,x;
}s[maxn];
int n,m,dep[maxn],son[maxn],siz[maxn],fa[maxn],tc[maxn],top[maxn],fc[maxn];
int head[maxn],to[maxn<<1],next[maxn<<1],c[maxn<<1],tt,now,nn;
int ch[maxn],ci,_m;int getint(){int w=0;bool q=0;char c=getchar();while((c>'9'||c<'0')&&c!='-') c=getchar();if(c=='-') c=getchar(),q=1;while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();return q?-w:w;
}void link(int x,int y){to[++tt]=y;next[tt]=head[x];head[x]=tt;to[++tt]=x,next[tt]=head[y];head[y]=tt;c[tt]=c[tt-1]=getint();
}void dfs(int u){siz[u]=1;for(int i=head[u],v;v=to[i],i;i=next[i])if(!siz[v]){dep[v]=dep[u]+1; fa[v]=u; fc[v]=c[i];dfs(v); siz[u]+=siz[v];if(siz[v]>siz[son[u]]) son[u]=v;}
}void dfs(int u,int d){top[u]=d;if(son[u]) tc[son[u]]=tc[u]+fc[son[u]],dfs(son[u],d);for(int i=head[u],v;v=to[i],i;i=next[i])if(!top[v]) dfs(v,v);
}int lca(int u,int v){now=0;while(top[u]!=top[v]){if(dep[fa[top[u]]]<dep[fa[top[v]]]) swap(u,v);now+=tc[u]+fc[top[u]]; u=fa[top[u]];}if(dep[u]>dep[v]) swap(u,v);now+=tc[v]-tc[u]; return u;
}void work(int u){for(int i=head[u],v;v=to[i],i;i=next[i])if(v!=fa[u])work(v),ch[u]+=ch[v],ch[v]=0;if(ch[u]==ci) _m=max(_m,fc[u]);
}bool pd(int x){ci=_m=ch[1]=0;for(int i=1;i<=m;i++)if(s[i].x>x){ch[s[i].u]++;ch[s[i].v]++;ch[s[i].g]-=2; ci++;}work(1);return nn-_m<=x;
}int main(){File("a");n=getint(); m=getint();for(int i=1;i<n;i++) link(getint(),getint());dep[1]=1;dfs(1); dfs(1,1);for(int i=1;i<=m;i++){s[i].u=getint(); s[i].v=getint();s[i].g=lca(s[i].u,s[i].v);nn=max(nn,s[i].x=now);}int l=0,r=nn,mid;while(l!=r){mid=l+r>>1;if(pd(mid)) r=mid;else l=mid+1;}printf("%d",l);
}

  不过这道题的标解的复杂度好像是并查集复杂度……留坑待填……

转载于:https://www.cnblogs.com/lcf-2000/p/5861051.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/395115.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css 属性选择器笔记

1、基本选择器&#xff1a; eg&#xff1a; *{margin:0;padding:0}p{color:black}.content{background:red;}#intro{padding-left:2em;} 2、多元素组合选择器 div p { color:#f00; }#nav li { display:inline; }#nav a { font-weight:bold; }div > strong { color:#f00; }h2…

scuba 报表_是否想了解JavaScript的for循环? 这个动画的SCUBA潜水员可以提供帮助!...

scuba 报表by Kevin Kononenko凯文科诺年科(Kevin Kononenko) 是否想了解JavaScript的for循环&#xff1f; 这个动画的SCUBA潜水员可以提供帮助&#xff01; (Want to learn about JavaScript’s for loops? This animated SCUBA diver can help!) For loops can be tough to…

力扣——寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。 请你找出这两个有序数组的中位数&#xff0c;并且要求算法的时间复杂度为 O(log(m n))。 你可以假设 nums1 和 nums2 不会同时为空。 示例 1: nums1 [1, 3] nums2 [2]则中位数是 2.0示例 2: nums1 [1, 2] nums2 [3, 4]…

uva-10152-乌龟排序

uva-10152-乌龟排序 求从待排序的到期望的顺序的最小操作顺序,只能进行一个操作,将当前的乌龟拿出来,上面的下移,拿出来的放到最上面 发现voj没有PE, 解题方法,把俩个串反过来使用,从期望的顺序到待排序的顺序. AC:170ms #include <iostream> #include<stdio.h> #i…

笔记本win10玩红警黑屏_【买笔记本电脑差评真的有参考意义?】

每次推荐笔记本电脑都会遇到一个重要的问题就是&#xff1a;“大多数消费者会下意识的去看京东评论&#xff0c;参考买的人是怎么说的&#xff0c;往往会出现不懂电脑的人继续误导不懂的人&#xff0c;导致越来越多的人被误导”本文聊聊关于京东评论究竟有没有参考价值。1&…

2.sed命令

2.sed命令 sed基本用法&#xff1a; sed: Stream EDitor 行编辑器 (全屏编辑器: vi) sed: 模式空间 默认不编辑原文件&#xff0c;仅对模式空间中的数据做处理&#xff1b;而后&#xff0c;处理结束后&#xff0c;将模式空间打印至屏幕&#xff1b; sed [options] AddressComma…

因此,您是一名新软件工程师。 让我们面对一些事实,揭穿一些神话。

by Trey Huffine通过Trey Huffine 因此&#xff0c;您是一名新软件工程师。 让我们面对一些事实&#xff0c;揭穿一些神话。 (So you’re a new Software Engineer. Let’s face some facts and debunk some myths.) When we’re learning to become software engineers, we’…

java前端接收回显图片_图片上传并回显后端篇

图片上传并回显后端篇我们先看一下效果继上一篇的图片上传和回显&#xff0c;我们来实战一下图片上传的整个过程&#xff0c;今天我们将打通前后端&#xff0c;我们来真实的了解一下&#xff0c;我们上传的文件&#xff0c;是以什么样的形式上传到服务器&#xff0c;难道也是一…

关于scanf和cin的大数据读入效率

关于scanf和cin的大数据读入效率好多大佬都说scanf的读入效率比cin高&#xff0c;我也当练手&#xff0c;用书上的程序用了个测试&#xff0c;程序如下&#xff1a;#include<iostream>#include<ctime>#include<cstdio>#include<windows.h>using namesp…

OBJECT_ID()的使用方法

数据库中每个对像都有一个唯一的ID值&#xff0c;用Object_name(id)可以根据ID值得到对像的名称&#xff0c;object_id(name)可以根据对像名称得到对象的IDobject_id()只能返回用户创建的对像的ID,像以sys开头的表都是系统表所以返回不了的 如下列&#xff1a; select object_n…

Django之model补充:一对多、跨表操作

表结构概述 model.py : class Something(models.Model):name models.CharField(max_length32)class UserType(models.Model):caption models.CharField(max_length32)s models.ForeignKey(Something)# 超级管理员&#xff0c;普通用户&#xff0c;游客&#xff0c;黑河class…

农民约翰是一个惊人的会计_我的朋友约翰在CSS Grid中犯了一个错误。 不要像约翰-这样做。

农民约翰是一个惊人的会计It had been two years and John had no job.已经两年了&#xff0c;约翰没有工作。 John was a smart 20-something guy. Okay, he had a job — but it wasn’t one he liked. It was too monotonous and was not nearly creative enough. His day …

zip直链生成网站_手把手教你如何用飞桨自动生成二次元人物头像

【飞桨开发者说】李思佑&#xff0c;昆明理工大学信息与计算科学大四本科生&#xff1b;2018年和2019年两次获得全国大学生数学建模比赛国家二等奖&#xff1b;2020年美国数学建模比赛获M奖。指导老师&#xff1a;昆明理工大学理学院朱志宁想画出独一无二的动漫头像吗&#xff…

Gradle入门到实战(一) — 全面了解Gradle

声明&#xff1a;本文来自汪磊的博客&#xff0c;转载请注明出处 可关注个人公众号&#xff0c;那里更新更及时&#xff0c;阅读体验更好&#xff1a; 友情提示由于文章是从个人公众号拷贝过来整理的&#xff0c;发现图片没有正常显示&#xff0c;没关注公众号的同学可通过如下…

java 0-9所有排列_java实现:键盘输入从0~9中任意5个数,排列组合出所有不重复的组合,打印出来...

必有追加大分&#xff01;&#xff01;&#xff01;比如1.2.3.4.5共有120个组合12345&#xff0c;12354&#xff0c;12435&#xff0c;12453&#xff0c;12534&#xff0c;12543&#xff1b;13245&#xff0c;13254&#xff0c;13425&#xff0c;13452&#xff0c;13524&#x…

智能家居物联网化将成为AWE大会最大看点

AWE大会已经在今日9点半开幕&#xff0c;AWE在今年将扩张至8个展馆&#xff0c;其整体展示规模达到11万平米&#xff0c;这是以往都无法匹敌的。海尔、美的、格力、海信、创维、TCL、康佳、格兰仕、澳柯玛、新飞、美菱、奥马、方太、老板、万和、万家乐、华帝、帅康、樱花、格美…

PHP 命名空间(namespace)

PHP 命名空间(namespace) PHP 命名空间(namespace)是在PHP 5.3中加入的&#xff0c;如果你学过C#和Java&#xff0c;那命名空间就不算什么新事物。 不过在PHP当中还是有着相当重要的意义。 PHP 命名空间可以解决以下两类问题&#xff1a; 用户编写的代码与PHP内部的类/函数/常量…

给matrix重新列名_如何认真升级Mac终端(甚至给它一个Matrix主题)

给matrix重新列名by Marcus Gardiner通过马库斯加德纳(Marcus Gardiner) 如何认真升级Mac终端(甚至给它一个Matrix主题) (How to seriously upgrade your Mac terminal (and even give it a Matrix theme)) 蓝色药丸&#xff0c;红色药丸和通往极乐世界的3个步骤 (A Blue Pill…

javaweb 图书管理系统完整代码_看一名Java开发人员以红队思维五分钟审计一套代码(续)...

前言上篇文章的发布引起了很多读者的浏览&#xff0c;有很多读者也催更希望读到续集&#xff0c;作者也收获到读者的鼓励&#xff0c;说明这条路线对大家有帮助&#xff0c;是有意义的。所以&#xff0c;今天作者将继续阐述在审计Java代码时的思路。概述上篇文章所讲的SQL注入和…

爱立信数据分析解决方案抓住物联网发展机遇

爱立信在2016年1月6日至9日于美国拉斯维加斯举办的国际消费电子展&#xff08;CES&#xff09;上推出“用户和物联网数据分析”解决方案。该解决方案将能帮助运营商提高对用户和物联网终端的内部管理效率&#xff0c;同时探索跨越多个垂直领域的新型物联网应用。 用户和物联网数…