题目描述 Description
公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰 。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间 。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始 ,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?
输入描述 Input Description
第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1<=ai,bi<=n 且 0<=ti<=1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1<=ui,vi<=n
输出描述 Output Description
输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。
样例输入 Sample Input
6 3 1 2 3 1 6 4 3 1 7 4 3 6 3 5 5 3 6 2 5 4 5
数据范围及提示 Data Size & Hint
样例解释:
将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。
将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。
将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。
将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。
将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。
故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。
测试数据及约定:
测试点编号
n=
m=
约定
1
100
1
2
100
100
第i条航道连接i号星球与i+1号星球
3
100
100
4
2000
1
5
1000
1000
第i条航道连接i号星球与i+1号星球
6
2000
2000
第i条航道连接i号星球与i+1号星球
7
3000
3000
第i条航道连接i号星球与i+1号星球
8
1000
1000
9
2000
2000
10
3000
3000
11
80000
1
12
100000
1
13
70000
70000
第i条航道连接i号星球与i+1号星球
14
80000
80000
第i条航道连接i号星球与i+1号星球
15
90000
90000
第i条航道连接i号星球与i+1号星球
16
100000
100000
第i条航道连接i号星球与i+1号星球
17
80000
80000
18
90000
90000
19
100000
100000
20
300000
300000
所有数据
1<=ai,bi,uj,vj<=n,0<=ti<=1000
(所有测试点编号加10)
95分题解:
LCA倍增+二分 只能95分了,最后一个点卡常数,T掉了
先LCA一遍,记下每个任务的起点,终点,公共祖先,所需时间(路程)
对于每个ans 统计>他的路径条数 cnt 并维护最大差值 dec
并且对于每条不合法的路径维护每个点的经过次数
然后枚举点 如果经过次数==cnt说明每一条不合法的都经过他
然后尝试把它建成虫洞 如果他对应边的权值>=dec 那么我们删掉它ans就合法了
统计不满足答案的任务cnt,然后维护一个sum[i]
关键是统计每个点在非法路径中的经过次数 :
维护sum数组 对于每个非法的路径起点a 终点b LCA(a,b)==s sum[a]++ sum[b]++ sum[s]-=2
这样往上更新的话 经过的点的sum值都变成1 祖先s的变成0
并将它们的sum值传到父亲结点,最后看是否能找出某个点i,使sum[i]=tot并且
连到这个点的边权值>= 最大任务时间-答案(即最大差值 dec),如果能,这个答案即为可行答案。
95分+5分(打表)=100分 代码:
#include<cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 300010
inline const int read(){
register int x=0 ,f=1 ;
register char ch=getchar();
while (ch<' 0 ' ||ch>' 9 ' ){if (ch==' - ' )f=-1 ;ch=getchar();}
while (ch>=' 0 ' &&ch<=' 9 ' ){x=x*10 +ch-' 0 ' ;ch=getchar();}
return x*f;
}
int n,m,tot;
struct ss{
int a,b,anc,di;
}lca[N];
struct node{
int v,t,next;
}e[N <<1 ];
int b[N],head[N];
int dep[N],dis[N],f[N][21 ];
int l,r,mid,ans,sum[N];
void add(int x,int y,int z){
e[ ++tot].v=y;
e[tot].t =z;
e[tot].next =head[x];
head[x] =tot;
}
void dfs(int x,int from ,int de,int l){
dep[x] =de;
dis[x] =l;
f[x][ 0 ]=from ;
for (int i=head[x];i;i=e[i].next){
if (e[i].v!=from ){
b[e[i].v] =i;// 与该点相连的边 给其编号
dfs(e[i].v,x,de+1 ,l+e[i].t);
}
}
}
int calc_lca(int a,int b){
if (dep[a]<dep[b]) swap(a,b);
int t=dep[a]-dep[b];
for (int i=0 ;i<=20 ;i++){
if ((1 <<i)&t){
a =f[a][i];
}
}
if (a==b) return a;
for (int i=20 ;i>=0 ;i--){
if (f[a][i]!=f[b][i]){
a =f[a][i];
b =f[b][i];
}
}
return f[a][0 ];
}
void updata(int now,int from ){// 利用反栈,更新一下sum[]
for (int i=head[now];i;i=e[i].next){
if (e[i].v!=from ){
updata(e[i].v,now);
sum[now] +=sum[e[i].v];
}
}
}
bool check(int x){
int cnt=0 ,dec=0 ;//
memset(sum,0 ,sizeof sum);
for (int i=1 ;i<=n;i++){
if (lca[i].di>x){
cnt ++;
sum[lca[i].a] ++;
sum[lca[i].b] ++;
sum[lca[i].anc] -=2 ;
dec =max(dec,lca[i].di-x);
}
}
updata( 1 ,1 );
for (int i=1 ;i<=n;i++) if (sum[i]==cnt&&e[b[i]].t>=dec) return 1 ;
return 0 ;
}
int main(){
n =read();m=read();
if (n==300000 &&m==300000 ){puts(" 142501313 " );return 0 ;}// point 20 listed
for (int i=1 ,x,y,z;i<n;i++){
x =read();y=read();z=read();
add(x,y,z);add(y,x,z);
}
dfs( 1 ,1 ,0 ,0 );
for (int j=1 ;j<=20 ;j++){
for (int i=1 ;i<=n;i++){
f[i][j] =f[f[i][j-1 ]][j-1 ];
}
}
for (int i=1 ;i<=m;i++){
lca[i].a =read();
lca[i].b =read();
lca[i].anc =calc_lca(lca[i].a,lca[i].b);
lca[i].di =dis[lca[i].a]+dis[lca[i].b]-(dis[lca[i].anc]<<1 );
r =max(r,lca[i].di);
}
r ++;
while (l<r){
mid =(l+r>>1 );
if (check(mid)) ans=r=mid;
else l=mid+1 ;
}
printf( " %d\n " ,ans);
return 0 ;
}
二分+树链剖分求LCA(避免卡常)+差分
AC代码:
2017-04-06
#include<cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N=3e5+5 ,M=N<<1 ;
int n,m,Q,cnt,ans,maxcost,sum[N],siz[N],son[N],dis[N],top[N],id[N],dep[N],fa[N];
struct data{int s,t,lca,dis;}d[N];
struct edge{int v,w,next;}e[M];int tot,head[N];
inline int read(){
int x=0 ,f=1 ;char ch=getchar();
while (ch<' 0 ' ||ch>' 9 ' ){if (ch==' - ' )f=-1 ;ch=getchar();}
while (ch>=' 0 ' &&ch<=' 9 ' ){x=x*10 +ch-' 0 ' ;ch=getchar();}
return x*f;
}
void add(int x,int y,int z){
e[ ++tot].v=y;e[tot].w=z;e[tot].next=head[x];head[x]=tot;
e[ ++tot].v=x;e[tot].w=z;e[tot].next=head[y];head[y]=tot;
}
void dfs(int x,int f,int de,int l){
fa[x] =f;dep[x]=de;dis[x]=l;siz[x]=1 ;
for (int i=head[x];i;i=e[i].next){
if (e[i].v!=f){
dfs(e[i].v,x,de +1 ,l+e[i].w);
siz[x] +=siz[e[i].v];
if (siz[son[x]]<siz[e[i].v]) son[x]=e[i].v;
}
}
}
void getpos(int x,int tp){
top[x] =tp;
id[ ++cnt]=x;
if (!son[x]) return ;
getpos(son[x],tp);
for (int i=head[x];i;i=e[i].next){
if (e[i].v!=fa[x]&&e[i].v!=son[x]){
getpos(e[i].v,e[i].v);
}
}
}
int lca(int x,int y){
for (;top[x]!=top[y];x=fa[top[x]]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
}
return dep[x]<dep[y]?x:y;
}
bool check(int ans){
int cnt=0 ;
memset(sum, 0 ,n+1 <<2 );
for (int i=1 ;i<=m;i++){
if (d[i].dis>ans){
++cnt;
sum[d[i].s] ++;
sum[d[i].t] ++;
sum[d[i].lca] -=2 ;
}
}
for (int i=n;i;i--) sum[fa[id[i]]]+=sum[id[i]];
int dec=maxcost-ans;
for (int i=1 ;i<=n;i++) if (sum[i]==cnt&&dis[i]-dis[fa[i]]>=dec) return 1 ;
return 0 ;
}
int main(){
n =read();m=read();
for (int i=1 ,x,y,z;i<n;i++){
x =read();y=read();z=read();
add(x,y,z);
}
dfs( 1 ,1 ,1 ,0 );getpos(1 ,1 );
for (int i=1 ;i<=m;i++){
d[i].s =read();
d[i].t =read();
d[i].lca =lca(d[i].s,d[i].t);
d[i].dis =dis[d[i].s]+dis[d[i].t]-(dis[d[i].lca]<<1 );
maxcost =max(maxcost,d[i].dis);
}
int l=0 ,r=maxcost,mid;
while (l<=r){
mid =l+r>>1 ;
if (check(mid)) r=mid-1 ,ans=mid;
else l=mid+1 ;
}
printf( " %d\n " ,ans);
return 0 ;
}
#include<cstdio>
#include <cstring>
#include <iostream>
#define R register
using namespace std;
int read(){
R int x=0 ;bool f=1 ;
R char ch=getchar();
while (ch<' 0 ' ||ch>' 9 ' ){if (ch==' - ' )f=0 ;ch=getchar();}
while (ch>=' 0 ' &&ch<=' 9 ' ){x=(x<<3 )+(x<<1 )+ch-' 0 ' ;ch=getchar();}
return f?x:-x;
}
const int N=3e5+10 ;
struct node{
int v,dis,next;
}e[N <<1 ];int tot,cnt;
int n,m,head[N];
int dep[N],top[N],siz[N],son[N],fa[N];
int id[N],num[N],val[N],dis[N],dist[N];
int maxcost,from [N],to[N];
void add(int x,int y,int z){
e[ ++tot]=(node){y,z,head[x]};
head[x] =tot;
}
void dfs(int x,int f,int de,int l){
fa[x] =f;dep[x]=de;dis[x]=dis[f]+l;dist[x]=l;siz[x]=1 ;
for (int i=head[x];i;i=e[i].next){
int v=e[i].v,w=e[i].dis;
if (v!=f){
dfs(v,x,de +1 ,w);
siz[x] +=siz[v];
if (!son[x]||siz[son[x]]<siz[v]) son[x]=v;
}
}
}
void getpos(int x,bool hson){
if (hson) top[x]=top[fa[x]];
else top[x]=x;
id[x] =++cnt;
if (son[x]) getpos(son[x],1 );
for (int i=head[x];i;i=e[i].next){
int v=e[i].v;
if (v!=fa[x]&&v!=son[x]){
getpos(v, 0 );
}
}
}
int lca(int x,int y){
for (;top[x]!=top[y];x=fa[top[x]]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
}
if (dep[x]>dep[y]) return y;
else return x;
}
void updata(int x,int y){
int ans=0 ;
for (;top[x]!=top[y];x=fa[top[x]]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
if (id[top[x]]<=id[x]) num[id[top[x]]]++,num[id[x]+1 ]--;
}
if (dep[x]>dep[y]) swap(x,y);
if (id[son[x]]<=id[y]) num[id[son[x]]]++,num[id[y]+1 ]--;
}
bool check(int mid){
memset(num, 0 ,sizeof num);
int cou=0 ;
for (int i=1 ;i<=m;i++){
if (val[i]>mid){
cou ++;
updata( from [i],to[i]);
}
}
if (!cou) return 1 ;
for (int i=1 ;i<=n;i++) num[i]+=num[i-1 ];
for (int i=2 ;i<=n;i++) if (num[id[i]]==cou){
if (maxcost-dist[i]<=mid) return 1 ;
}
return 0 ;
}
int l,r,mid,ans;
int main(){
n =read();m=read();
for (int i=1 ,x,y,z;i<n;i++){
x =read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}
dfs( 1 ,1 ,1 ,0 );
getpos( 1 ,0 );
for (int i=1 ;i<=m;i++){
from [i]=read();
to[i] =read();
val[i] =dis[from [i]]+dis[to[i]]-2 *dis[lca(from [i],to[i])];
maxcost =max(maxcost,val[i]);
}
l =0 ;r=maxcost;
while (l<=r){
mid =l+r>>1 ;
if (check(mid)) r=mid-1 ,ans=mid;
else l=mid+1 ;
}
printf( " %d\n " ,ans);
return 0 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术