这是一个很菜的 Oier 的博客|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

2022-10-10 10:06阅读: 88评论: 0推荐: 0

P2680 [NOIP2015 提高组] 运输计划

[NOIP2015 提高组] 运输计划

Luogu P2680

题目背景

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

题目描述

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

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

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

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

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

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

输入格式

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

接下来 n1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,biti,表示第 i 条双向航道修建在 aibi 两个星球之间,任意飞船驶过它所花费的时间为 ti

数据保证

接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 ujvj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。

输出格式

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

样例 #1

样例输入 #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

提示

所有测试数据的范围和特点如下表所示

请注意常数因子带来的程序效率上的影响。

对于 100% 的数据,保证:1ai,bin0ti10001ui,vin

Solution

一道树上差分的好的练手题。

先说明题意:求将树上一条边的边权设置为 0 后,最长的路径的最小值。

看到这种求的又是最大又是最小的题,基本都可以往二分答案想。此题的答案显然是具有单调性的,也就是说可以用二分答案解决。

考虑知道了最长路径的最小值,如何验证答案。显然每条路径的长度都可以通过预处理 LCA 的方式 O(1) 的算出,因此枚举每一条路径,如果当前路径的长度大于了答案,那么就将这条路径上的所有边都 +1(相当于是覆盖一次)。如果枚举完所有的路径后没有发现这样的情况,就说明当前情况是合法的,将答案的上界减小。如果存在这样的路径,那么就对树上所有边扫描一遍,如果有一条边被所有这样的路径覆盖,并且将这条边的边权改为 0 后答案是合法的,就说明当前答案是合适的。如果不存在,就将下界提高。

现在考虑如何实现覆盖的操作(类似于区间加),可以用树剖 O(log2n) 在线的解决,但是很显然,我们的询问是在所有操作结束后,也就是说可以使用树上差分的技巧 O(1) 的修改,最后统一 O(n) 解决。不过一般的树上差分都是对点权进行差分,而此题要求对边权差分,不过有一个常用的 trick,就是将每条边的边权作为端点中更深的一个的点权,不难发现,这样每条边权都对应到了一个点权,并且根节点的点权为 0。那么对于一条路径 (a,b),就可以将 a,b 点的差分数组 +1,将 lca(a,b) 处的 2(至于此处为什么与树上点差分不同,画一下图就明白了)。

所以就可以很轻松的写出代码了。

Code

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
	k=0;T flag=1;char b=getchar();
	while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
	while (isdigit(b)) {k=k*10+b-48;b=getchar();}
	k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=3e5;
struct EDGE{
	int nxt,to,l;
}edge[(_SIZE<<1)+5];
int tot,head[_SIZE+5];
void AddEdge(int x,int y,int l) {edge[++tot]=(EDGE){head[x],y,l};head[x]=tot;}
struct LINK{
	int x,y,lca;
}l[_SIZE+5]; 
int n,m,f[_SIZE+5][25],dep[_SIZE+5],dis[_SIZE+5],pre[_SIZE+5];
void dfsForLca(int x,int fa)
{
	f[x][0]=fa,dep[x]=dep[fa]+1;
	for (int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1];
	for (int i=head[x];i;i=edge[i].nxt) 
	{
		int twd=edge[i].to;
		if (twd==fa) continue;
		dis[twd]+=dis[x]+edge[i].l;
		pre[twd]=edge[i].l;
		dfsForLca(twd,x);
	}
}
int LCA(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=0,temp=dep[x]-dep[y];temp;i++,temp>>=1) if (temp&1) x=f[x][i];
	if (x==y) return x;
	for (int i=20;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
bool flag=0;
int diff[_SIZE+5];
int judge(int x,int fa,int cnt,int maxn)
{
	int curSum=diff[x];
	for (int i=head[x];i;i=edge[i].nxt)
	{
		int twd=edge[i].to;
		if (twd==fa) continue;
		curSum+=judge(twd,x,cnt,maxn);
	}
	if (curSum>=cnt && pre[x]>=maxn) flag=1;
	return curSum;
}
bool check(int limit)
{
	mem(diff,0);
	int cnt=0,maxn=0;flag=0;
	for (int i=1;i<=m;i++)
	{
		int x=l[i].x,y=l[i].y,lca=l[i].lca;
		if (dis[x]+dis[y]-2*dis[lca]>limit)
		{
			diff[x]++,diff[y]++,diff[lca]-=2;cnt++;
			maxn=max(maxn,dis[x]+dis[y]-2*dis[lca]);
		}
	}
	if (cnt==0) return 1;
	judge(1,0,cnt,maxn-limit);
	return flag;
}
signed main()
{
	read(n);read(m);
	for (int i=1;i<n;i++)
	{
		int u,v,l;read(u),read(v),read(l);
		AddEdge(u,v,l),AddEdge(v,u,l);
	}
	dfsForLca(1,0);
	for (int i=1;i<=m;i++)
	{
		read(l[i].x),read(l[i].y);
		l[i].lca=LCA(l[i].x,l[i].y);
	}
	int l=0,r=3e8;
	while (l<r)
	{
		int mid=(l+r)>>1;
		if (check(mid)) r=mid;
		else l=mid+1;
	}
	writewith(l,'\n');
	return 0;
}
posted @   Hanx16Msgr  阅读(88)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起