【Atcoder - AGC002D】Stamp Rally

版权声明:本文为博主原创文章……懂吗?要尊重别人的劳动成果呐 https://blog.csdn.net/Tiw_Air_Op1721/article/details/84189209


@Problem Statement@

We have an undirected graph with N vertices and M edges. The vertices are numbered 1 through N, and the edges are numbered 1 through M. Edge i connects vertices ai and bi. The graph is connected.
On this graph, Q pairs of brothers are participating in an activity called Stamp Rally. The Stamp Rally for the i-th pair will be as follows:

(1)One brother starts from vertex xi, and the other starts from vertex yi.
(2)The two explore the graph along the edges to visit zi vertices in total, including the starting vertices. Here, a vertex is counted only once, even if it is visited multiple times, or visited by both brothers.
(3)The score is defined as the largest index of the edges traversed by either of them. Their objective is to minimize this value.

Find the minimum possible score for each pair.

Constraints
3≤N≤10^5
N−1≤M≤10^5
1≤ai<bi≤N
The given graph is connected.
1≤Q≤10^5
1≤xj<yj≤N
3≤zj≤N

Input
The input is given from Standard Input in the following format:
N M
a1 b1
a2 b2
:
aM bM
Q
x1 y1 z1
x2 y2 z2
:
xQ yQ zQ
Output
Print Q lines. The i-th line should contain the minimum possible score for the i-th pair of brothers.

Sample Input 1
5 6
2 3
4 5
1 2
1 3
1 4
1 5
6
2 4 3
2 4 4
2 4 5
1 3 3
1 3 4
1 3 5
Sample Output 1
1
2
3
1
5
5

@Translation@

N 点 M 边的无向连通图。
现有 Q 组询问,每次询问(x, y, z)。找出以 x 为起点的一条路径与以 y 为起点的一条路径(路径可以重复经过结点),标记路径上的点后(包括起点),恰有 z 个被标记的点。
最小化两条路径上编号最大的边,求这个编号。

@Solution@

最小化一个最大值,显然是二分。二分后,我们可以用并查集求出答案进行判定。
多组询问 + 二分,不得不联想到整体二分。

然而我还没有学过。只能把这道题当作整体二分的入门题……

我们对于每一层,存储一个并查集,这样并查集就用不着撤回了。

还有就是即使到达了叶子依然要加入结点到并查集中。

具体见代码。

@Knowledge@

【以下是我对整体二分的理解】
【如果想直接看代码可以跳过这一段】

我们先来观察我们二分时的决策树。二分的过程就对应着根到叶子的一个路径。
整体二分 - 图示
整体二分用的是这样一个离线的思想:某一结点收集需要的信息需要较高的时间复杂度,但是多次二分所经过的结点有重复的情况,导致我们需要多次重复收集结点信息,从而升高了算法时间复杂度。因此我们统一收集某个结点的信息,再判断在这个结点内的询问是往左子树走还是往右子树走。

假如每个结点 [le, ri] 收集信息的时间复杂度是与(ri-le+1)相关的线性时间复杂度,则整体二分的时间效率是很高的。
为什么呢?对应上面的决策树,我们每一层(注意不是每个结点)的时间复杂度均摊下来就是 (R-L+1) 的线性时间复杂度,且总共有 log 层,还是线性级别的时间复杂度。
同时,我们每个询问每一层恰被处理一次,所以时间复杂度为 (log*询问次数)。

有时整体二分还会涉及 cdq 分治的思想,即用左边的去更新右边的(比如需要收集 <= mid 的信息)。

@Code@

如果对我的题解或者代码仍抱有疑问,欢迎在下面留言进行询问qwq。

#include<cstdio>
const int MAXN = 100000;
struct edge{
	int u, v;	
}edges[MAXN + 5];
int fa[20][MAXN + 5], siz[20][MAXN + 5];
int Find(int x, int type) {
	return fa[type][x] == x ? x : fa[type][x] = Find(fa[type][x], type);
}
void Union(int x, int y, int type) {
	int fx = Find(x, type), fy = Find(y, type);
	if( fx != fy ) {
		siz[type][fy] += siz[type][fx];
		fa[type][fx] = fy;
	}
}
struct query{
	int x, y, z;
	int ind;
}qry[MAXN + 5], tmp[MAXN + 5];
int ans[MAXN + 5];
bool Check(query q, int type) {
	if( Find(q.x, type) == Find(q.y, type) )
		return siz[type][Find(q.x, type)] >= q.z;
	else return siz[type][Find(q.x, type)] + siz[type][Find(q.y, type)] >= q.z;
}
void Divide_Conquer(int L, int R, int le, int ri, int dep) {
	if( le == ri ) {
		for(int i=L;i<=R;i++)
			ans[qry[i].ind] = le;
		Union(edges[le].u, edges[le].v, dep);//It's necessary!!!
		return ;
	}
	int mid = (le + ri) >> 1;
	for(int i=le;i<=mid;i++)
		Union(edges[i].u, edges[i].v, dep);
	int p = L-1, q = R+1;
	for(int i=L;i<=R;i++)
		if( Check(qry[i], dep) ) tmp[++p] = qry[i];
		else tmp[--q] = qry[i];
	for(int i=L;i<=R;i++)
		qry[i] = tmp[i];
	for(int i=mid+1;i<=ri;i++)
		Union(edges[i].u, edges[i].v, dep);
	Divide_Conquer(L, p, le, mid, dep+1);
	Divide_Conquer(q, R, mid+1, ri, dep+1);
}
int main() {
	int N, M, Q;
	scanf("%d%d", &N, &M);
	for(int i=1;i<=N;i++)
		for(int j=0;j<20;j++)
			fa[j][i] = i, siz[j][i] = 1;
	for(int i=1;i<=M;i++)
		scanf("%d%d", &edges[i].u, &edges[i].v);
	scanf("%d", &Q);
	for(int i=1;i<=Q;i++) {
		scanf("%d%d%d", &qry[i].x, &qry[i].y, &qry[i].z);
		qry[i].ind = i;
	}
	Divide_Conquer(1, Q, 1, M, 0);
	for(int i=1;i<=Q;i++)
		printf("%d\n", ans[i]);
}

@End@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

猜你喜欢

转载自blog.csdn.net/Tiw_Air_Op1721/article/details/84189209
今日推荐