首页 > 其他分享 >P1967 [NOIP2013 提高组] 货车运输 题解

P1967 [NOIP2013 提高组] 货车运输 题解

时间:2022-08-18 23:56:32浏览次数:61  
标签:10 le idx sz int 题解 货车运输 son P1967

题目描述

A 国有 \(n\) 座城市,编号从 \(1\) 到 \(n\),城市之间有 \(m\) 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 \(q\) 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

第一行有两个用一个空格隔开的整数 $ n,m$,表示 \(A\) 国有 $ n$ 座城市和 \(m\) 条道路。

接下来 \(m\) 行每行三个整数 \(x, y, z\),每两个整数之间用一个空格隔开,表示从 $x $ 号城市到 $ y $ 号城市有一条限重为 \(z\) 的道路。
注意: \(x \neq y\),两座城市之间可能有多条道路 。

接下来一行有一个整数 \(q\),表示有 \(q\) 辆货车需要运货。

接下来 \(q\) 行,每行两个整数 \(x,y\),之间用一个空格隔开,表示一辆货车需要从 \(x\) 城市运输货物到 \(y\) 城市,保证 \(x \neq y\)

输出格式

共有 \(q\) 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出 \(-1\)。

样例输入

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

样例输出

3
-1
3

对于 \(100\%\) 的数据,\(1 \le n < 10^4\),\(1 \le m < 5\times 10^4\),$1 \le q< 3\times 10^4 $,\(0 \le z \le 10^5\)。


Kruskal 重构树板子题,但我不想动脑子,所以使用 Kruskal 求最大生成树 + 树链剖分维护边权 + 线段树维护静态最小值。跑最大生成树的原因显然,贪心思想跟路径远近无关的情况下应该尽量沿着边权大的走,但限制两个点之间的距离承重又是两条路之间最小的那个决定的,原因它在一棵生成树上,两点之间有且只有一条路。

说点细节,树剖维护边权是要在第一遍 dfs 时将边权换为子节点点权,然后在第二遍 dfs 针对点权建立正反映射。

还有,查询时最后是从 \([id[v]+1,id[u]]\) 加一,加一,加一,剩下地方不变,不变,不变。

原题构造出来的可能是一颗森林所以需要以所有并查集的父节点都跑一遍树剖。

Code.

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=5e4+10,imax=INT_MAX;
int n,m,h[N],ne[N<<1],e[N<<1],w[N<<1],idx,p[N],dep[N],sz[N],fa[N],son[N],id[N],cnt,yl[N],top[N],val[N],q;
struct node
{
	int u,v,w;
	bool operator < (const node &o) const{
		return w > o.w;
	}
} pl[M];
void add(int u,int v,int c) {ne[++idx]=h[u],e[idx]=v,w[idx]=c,h[u]=idx;}
int find(int x) {if(p[x] != x) p[x]=find(p[x]); return p[x];}
void dfs1(int u,int father,int depth)
{
	dep[u]=depth; sz[u]=1; fa[u]=father;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i]; if(j == father) continue ;
		val[j]=w[i]; dfs1(j,u,depth+1); sz[u]+=sz[j];
		if(sz[son[u]] < sz[j]) son[u]=j;
	}
}
void dfs2(int u,int t)
{
	id[u]=++cnt; yl[cnt]=val[u]; top[u]=t;
	if(! son[u]) return ; dfs2(son[u],t);
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i]; if(j == son[u] || j == fa[u]) continue ;
		dfs2(j,j);
	}
}
struct Seg
{
	struct node {int l,r,mn;} tr[N<<2];
	void pushup(int u) {tr[u].mn=min(tr[u<<1].mn,tr[u<<1|1].mn);}
	void build(int u,int l,int r)
	{
		tr[u].l=l,tr[u].r=r,tr[u].mn=imax;
		if(l == r) return tr[u].mn=yl[l],void();
		int mid = l + r >> 1;
		build(u<<1,l,mid); build(u<<1|1,mid+1,r);
		pushup(u);
	}
	int query(int u,int l,int r)
	{
		if(l <= tr[u].l && tr[u].r <= r) return tr[u].mn;
		int mid = tr[u].l + tr[u].r >> 1,res=imax;
		if(l<=mid) res=min(res,query(u<<1,l,r)); if(r>mid) res=min(res,query(u<<1|1,l,r));
		return res;
	}
} st;
int query(int u,int v)
{
	int res=imax;
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]]) swap(u,v);
		res=min(res,st.query(1,id[top[u]],id[u]));
		u=fa[top[u]];
	}
	if(dep[u] < dep[v]) swap(u,v);
	res=min(res,st.query(1,id[v]+1,id[u]));
	return res;
}
int main()
{
	memset(h,-1,sizeof h); scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) p[i]=i;
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&pl[i].u,&pl[i].v,&pl[i].w);
	stable_sort(pl+1,pl+1+m);
	for(int i=1;i<=m;i++)
	{
		int u=find(pl[i].u),v=find(pl[i].v);
		if(u == v) continue ; p[u]=v;
		add(pl[i].u,pl[i].v,pl[i].w);
		add(pl[i].v,pl[i].u,pl[i].w);
	}
	for(int i=1;i<=n;i++)
	{
		if(p[i] != i) continue ; val[i]=imax;
		dfs1(i,0,1); dfs2(i,i);
	}
	st.build(1,1,n);
	scanf("%d",&q);
	while(q -- )
	{
		int u,v; scanf("%d%d",&u,&v);
		if(p[find(u)] != p[find(v)]) puts("-1");
		else printf("%d\n",query(u,v));
	}
	return 0;
}

标签:10,le,idx,sz,int,题解,货车运输,son,P1967
From: https://www.cnblogs.com/EastPorridge/p/16600581.html

相关文章

  • 题解P2143 [JSOI2010] 巨额奖金
    题意就是让你求有多少种最小生成树生成树用kruskal求就好了我们考虑用dfs中用乘法原理去计数#include<bits/stdc++.h>#defineN1000100usingnamespacestd;ty......
  • ARC097E题解
    感觉挺一眼的啊?众所周知如果序列\(i\)要通过相邻两项交换变成\(p_i\),那么交换次数就是\(\sum_{i<j}[p_i>p_j]\),或者说线段\((i,p_i)\)相交的对数。于是一个很naiv......
  • 【题解】 洛谷P3694 邦邦的大合唱站队
    发现尽管\(n\)比较大,但\(m\)非常小,于是考虑状压。记\(dp_{i}\)表示满足条件的乐队集合为\(i\)时的最小出队人数,\(dp_i=\min\{dp_{i\\xor\\1<<k}\}+w_{i\\xo......
  • [CF1450F] The Struggling Contestant 题解
    \(\mathtt{Link}\)CF1450FTheStrugglingContestant-洛谷|计算机科学教育新生态(luogu.com.cn)\(\mathtt{Description}\)\(T\)组数据。一共有\(n\)道题,题号......
  • P5080 Tweetuzki 爱序列 题解
    题目传送门更好地阅读体验题目大意Tweetuzki有一个长度为\(n\)的序列\(a_1,a_2,\cdots,a_n\)。他希望找出一个最大的\(k\),满足在原序列中存在一些数\(b_1,b......
  • IOI 2022 简要题解
    考前写题解增加RP。D1T1:考虑按照列DP。对于每一列选择的鱼的区间进行决策。每列中被选择的y坐标最大的鱼,需要被左面或右面覆盖。假设我们决策好了前i列的方案,考虑第i列......
  • [题解] HDU 5115 Dire Wolf 区间DP
    考虑先枚举所有的物品中最后拿走的,这样就分成了2个子问题,即先拿完左边的,再拿完右边的,最后拿选出的那个。令dp(i,j)表示拿完[i,j]所有物品的最小代价。你可能会说,我们拿[i,j......
  • 题解 CF1575D【Divisible by Twenty-Five】
    值域非常小,其中只有\(4\times10^6\)个数是\(25\)的倍数,因此可以暴力枚举所有位数正确的\(25\)的倍数,然后检查是否合法。检查方法就是枚举每一位,如果是数字就必须一......
  • CF Round 814 Div2 题解
    A题ChipGame(签到)给定一个\(n\)行\(m\)列的方格矩阵,左下角是\((1,1)\),右上角是\((m,n)\)。(采取了类似笛卡尔坐标系的表示法,不是普通的\(x\)行\(y\)列)Burenka......
  • CF578C题解
    看到题面的第一眼是这玩意儿关于x是单谷的,证明稍微想了一下:设\(f[k]\)和\(g[k]\)是原序列中长度为\(k\)的子区间的最大子区间和最小子区间,给定\(x\)时答案就相......