[NOIP2013提高组]货车运输

题目:洛谷P1967、Vijos P1843、codevs3287。

题目大意:有n个城市m条道路,每条道路有一个限重,规定货车运货不能超过限重。有一些询问,问你两个城市之间一次最多能运多少重的货(可能无法到达)。

解题思路:首先,要保证原来连通的点连通,限重要尽可能大,所以最大生成树。然后对每个询问找两个点的最近公共祖先,然后求出两点路径上最大限重的最小值即可。

用倍增求LCA,可以边算边求出最小值,不用用一些复杂的方法。代码中我用sml[x][i]表示x和它的第$2^i$个祖先之间的路径上最大限重的最小值。然后在倍增时更新答案即可。详见代码

C++ Code:

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cstdio>
using std::sort;
using std::swap;
struct edge{
	int u,v,t;
	bool operator < (const edge& rhs)const{return t>rhs.t;}
}e[50005];
struct tree_edge{
	int to,dist,nxt;
}E[120005];
int n,m,fa[10005],head[10005]={0},cnt=0,ans,deep[10005],p[10005][16],sml[10005][16];
inline int min(int a,int b){return(a<b)?(a):(b);}
inline int readint(){
	char c=getchar();
	int p=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())p=(p<<3)+(p<<1)+(c^‘0‘);
	return p;
}
int dad(int x){return(fa[x]==x)?(x):(fa[x]=dad(fa[x]));}
inline int addedge(int from,int to,int dist){
	E[++cnt]=(tree_edge){to,dist,head[from]};
	head[from]=cnt;
	E[++cnt]=(tree_edge){from,dist,head[to]};
	head[to]=cnt;
}
void dfs(int u){
	for(int i=head[u];i;i=E[i].nxt)
	if(!deep[E[i].to]){
		deep[E[i].to]=deep[u]+1;
		p[E[i].to][0]=u;
		sml[E[i].to][0]=E[i].dist;
		dfs(E[i].to);
	}
}
void init(){
	for(int j=1;(1<<j)<=n;++j)
	for(int i=1;i<=n;++i)
	if(p[i][j-1]!=-1)
	p[i][j]=p[p[i][j-1]][j-1],sml[i][j]=min(sml[i][j-1],sml[p[i][j-1]][j-1]);
}
int lca(int x,int y,int& ans){
	ans=2000000000;
	int i;
	if(deep[x]<deep[y])swap(x,y);
	for(i=0;(1<<i)<=n;++i);--i;
	for(int j=i;j>=0;--j)
	if(deep[p[x][j]]>=deep[y]){
		ans=min(ans,sml[x][j]),x=p[x][j];
	}
	if(x==y)return x;
	for(int j=i;j>=0;--j)
	if(p[x][j]!=p[y][j]&&p[x][j]!=-1){
		ans=min(ans,min(sml[x][j],sml[y][j]));
		x=p[x][j];
		y=p[y][j];
	}
	ans=min(ans,min(sml[x][0],sml[y][0]));
	return p[x][0];
}
int main(){
	n=readint(),m=readint();
	for(int i=1;i<=m;++i)e[i].u=readint(),e[i].v=readint(),e[i].t=readint();
	sort(e+1,e+m+1);
	for(int i=1;i<=n;++i)fa[i]=i;
	for(int okE=1,now=1;now<=m;++now){
		int a=dad(e[now].u),b=dad(e[now].v);
		if(a!=b){
			fa[b]=a;
			addedge(e[now].u,e[now].v,e[now].t);
			++okE;
		}
		if(okE==n)break;
	}
	int Q=readint();
	memset(deep,0,sizeof deep);
	memset(p,-1,sizeof p);
	memset(sml,0x3f,sizeof sml);
	for(int i=1;i<=n;++i)
	if(!deep[i]){
		deep[i]=1;
		dfs(i);
	}
	init();
	while(Q--){
		int x=readint(),y=readint();
		int a=dad(x),b=dad(y);
		if(a!=b){
			puts("-1");
			continue;
		}
		lca(x,y,ans);
		printf("%d\n",ans);
	}
}
时间: 2024-10-12 12:14:42

[NOIP2013提高组]货车运输的相关文章

洛谷P1967 [NOIP2013提高组Day1T2]货车运输

P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y

【解题】noip2013提高组(day1+day2)

这套题,勾起了我无数美好的回忆←意思是该好好复习了... [day1] 一.转圈游戏 首先,第一题,在处理k的时候应该用快速幂算法. 大概就是下面这样,要注意的是:1.二分时要判断有无余数.2.先设数,在进行乘积运算,不然会递归两次=.= 1 int pow(int a,int pos) 2 { 3 if(pos==1) return a%t; 4 int temp=pow(a,pos/2); 5 if(pos%2==1) return (temp*temp*a)%t; 6 return (te

noip2013 提高组

T1 转圈游戏 题目传送门 果不其然 第一题还是模拟题 一波快速幂解决问题 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int read(){ int ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();} while(c>='0'&&c&

【NOIP2013提高组】货车运输

https://www.luogu.org/problem/show?pid=1967 思考一下,将图的所有边按边权从大到小依次加入图,则当u与v第一次连通时,刚加入的边就是使u与v两点的路径中的最小边最大的边. 将图的所有边按边权从大到小依次加入图?这不就是Kruscal算法最大生成树吗! 所以我们只需要对原图求最大生成树,对于每个询问求两点的路径上的最小边就可以了.虽然可以用树剖+ST表优化,但是暴力求LCA+暴力爬链也能过了. 暴力LCA+暴力爬链: #include <algorithm

洛谷 P1969 积木大赛(NOIp2013提高组D2T1)

题目描述 春春幼儿园举办了一年一度的"积木大赛".今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成n块高度为 0 的积木).接下来每次操作,小朋友们可以选择一段连续区间[l, r],然后将第第 L 块到第 R 块之间(含第 L 块和第 R 块)所有积木的高度分别增加1. 小 M 是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少.但她不是一个勤于动手的孩子,所

[部分题解]noip2013提高组Day2

积木大赛: 之前没有仔细地想,然后就直接暴力一点(骗点分),去扫每一高度,连到一起的个数,于是2组超时 先把暴力程序贴上来(可以当对拍机) 1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 FILE *fin = fopen("block.in","r"); 5 FILE *fout= fopen("block.out","w&quo

【NOIP2015提高组】运输计划

https://daniu.luogu.org/problem/show?pid=2680 使完成所有运输计划的时间最短,也就是使时间最长的运输计划耗时最短.最大值最小问题考虑用二分答案,每次check(mid)检查时间最长的运输计划耗时是否小于等于mid,二分出使得check(mid)==true的最小mid值. check函数怎么写是本题的难点.耗时小于mid的运输计划不会影响check的结果.耗时大于mid的运输计划肯定需要改造他们的共同边才有可能使它们耗时都小于mid,而有多条共同边的时

2016.7.12 NOIP2013提高组 day2解题报告(未完成版)

考试马不停蹄地到来,昨天的程序还没改完,今天又考了day2,虽然没有昨天那么懵逼,但还是不尽如人意,现在还没讲题,我打算先自己看一次解题报告,争取加深理解,毕竟一位前辈说过,做一套题的质量取决于题本身的质量和你思考的程度. 考试总结: 1.数据分析推测可行算法很重要,要灵活掌握常用算法的时间复杂度: 2.对拍的方式要熟练,写对拍耗费的时间过多: 3.要加强代码实现的能力,比较突出的表现就是写200-300行多函数模拟或搜索的能力: 4.不要急于写过不完的程序,要多拿一点时间来分析数据,样例不够还

noip2013——提高组——积木大赛

noip2013 D2T1 太简单了,不说了. #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int read(){ int t=1,num=0; char c=getchar(); while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();} while(c>=