1. 程式人生 > >【Atcoder - AGC002D】Stamp Rally

【Atcoder - AGC002D】Stamp Rally

@Stamp [email protected]


@Problem [email protected]

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

@[email protected]

N 點 M 邊的無向連通圖。
現有 Q 組詢問,每次詢問(x, y, z)。找出以 x 為起點的一條路徑與以 y 為起點的一條路徑(路徑可以重複經過結點),標記路徑上的點後(包括起點),恰有 z 個被標記的點。
最小化兩條路徑上編號最大的邊,求這個編號。

@[email protected]

最小化一個最大值,顯然是二分。二分後,我們可以用並查集求出答案進行判定。
多組詢問 + 二分,不得不聯想到整體二分。

然而我還沒有學過。只能把這道題當作整體二分的入門題……

我們對於每一層,儲存一個並查集,這樣並查集就用不著撤回了。

還有就是即使到達了葉子依然要加入結點到並查集中。

具體見程式碼。

@[email protected]

【以下是我對整體二分的理解】
【如果想直接看程式碼可以跳過這一段】

我們先來觀察我們二分時的決策樹。二分的過程就對應著根到葉子的一個路徑。
整體二分 - 圖示
整體二分用的是這樣一個離線的思想:某一結點收集需要的資訊需要較高的時間複雜度,但是多次二分所經過的結點有重複的情況,導致我們需要多次重複收集結點資訊,從而升高了演算法時間複雜度。因此我們統一收集某個結點的資訊,再判斷在這個結點內的詢問是往左子樹走還是往右子樹走。

假如每個結點 [le, ri] 收集資訊的時間複雜度是與(ri-le+1)相關的線性時間複雜度,則整體二分的時間效率是很高的。
為什麼呢?對應上面的決策樹,我們每一層(注意不是每個結點)的時間複雜度均攤下來就是 (R-L+1) 的線性時間複雜度,且總共有 log 層,還是線性級別的時間複雜度。
同時,我們每個詢問每一層恰被處理一次,所以時間複雜度為 (log*詢問次數)。

有時整體二分還會涉及 cdq 分治的思想,即用左邊的去更新右邊的(比如需要收集 <= mid 的資訊)。

@[email protected]

如果對我的題解或者程式碼仍抱有疑問,歡迎在下面留言進行詢問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]);
}

@[email protected]

就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。