1. 程式人生 > 實用技巧 >20200713 T3 圖論

20200713 T3 圖論

題目描述

\(\text{Tom}\) 熱愛出毒瘤題,但這次他似乎出了一個並不毒瘤的題。

給定一張 \(N\) 個點 \(M\) 條無向邊的圖,每條邊有邊權 \(w\)。定義一個連通塊的大小為該連通塊內所有邊權的和。

然後進行 \(K\) 組詢問。每次詢問在由所有邊權不超過 \(X_i\) 的邊構成的新的生成子圖中連通塊的大小的最大值。

輸入格式

\(1\)\(3\) 個整數 \(n,m,k\) 表示圖有 \(n\) 個點,\(m\) 條邊。有 k 組詢問。

\(2\) 行到第 \(m + 1\) 行每行三個正整數 \(u,v,w,\)表示點 \(u\) 和點 \(v\) 之間有一條邊權為 \(w\)

的邊。

\(m + 1\) 行到第 \(m + k\) 行每行一個整數 \(x\),表示在這次詢問中,只能使用邊權不超過 \(w\) 的邊。

可能存在重邊,但不存在自環。

輸出格式

\(K\) 行,對於每組詢問輸出一次答案。

樣例

input1

5 5 3
1 2 5
2 3 6
3 4 4
3 5 2
4 5 3
7
5
3

output1

20
9
5

資料規模和限制

對於全部測試資料,滿足 \(N,M,K \leq 100000\)\(W \leq1000\)

測試點 N M K
\(1\) \(\leq 10\) \(\leq 10\) \(\leq 10\)
\(2 \sim 3\)
\(\leq 1000\) \(\leq 1000\) \(\leq 1000\)
\(4 \sim 5\) \(\leq 1000\) \(\leq 1000\) \(\leq 100000\)
\(6 \sim 10\) \(\leq 100000\) \(\leq 100000\) \(\leq 100000\)

思路

並查集。

將詢問離線。從對邊權的限制由小到大處理,這樣就轉化成了不斷往一個圖中加邊,詢問你加了多少遍之後最大的連通塊的大小。

Code

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 100001

typedef int ll;
inline void read(ll &T) {
	ll x = 0;
	bool f = 0;
	char c = getchar();
	while(c < '0' || c > '9') {
		if (c == '-') f = !f;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	T = f ? -x : x;
}


inline void write(ll x) {
	if (x < 0) putchar('-'), write(-x);
	else {
		if (x / 10) write(x / 10);
		putchar(x % 10 + '0');
	}
}

int n, m, k;
ll ub[MAXN], ans[MAXN];
int fa[MAXN], size[MAXN];
struct P {
	int u, v;
	ll w;
	friend bool operator < (P x, P y) {
		return x.w < y.w;
	}
}a[MAXN];
struct query {
	int w, num;
	friend bool operator < (query x, query y) {
		return x.w < y.w;
	}
}q[MAXN];

int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

void Union(int x, int y) {
	if (x > y) std::swap(x, y);
	fa[y] = x;
}

ll max(ll a, ll b) { return a > b ? a : b; }

int main() {
	//freopen("Graph.in", "r", stdin);
	//freopen("Graph.out", "w", stdout);
	scanf("%d %d %d", &n, &m, &k);
	for (int i = 1; i <= n; ++i) fa[i] = i;
	for (int i = 1; i <= m; ++i) {
		scanf("%d %d", &a[i].u, &a[i].v);
		read(a[i].w);
	}
	std::sort(a + 1, a + m + 1);
	for (int i = 1; i <= k; ++i) {
		scanf("%d", &q[i].w);
		q[i].num = i;
	}
	std::sort(q + 1, q + k + 1);
	int now = 1; ll maxx = 0;
	for (int i = 1; i <= k; ++i) {
		while(q[i].w >= a[now].w && now <= m) {
			int rootx = find(a[now].u), rooty = find(a[now].v);
			if (rootx != rooty) Union(rootx, rooty);
			if (rootx < rooty) {
				ub[rootx] += a[now].w;
				ub[rootx] += ub[rooty];
				ub[rooty] = 0;
				maxx = max(maxx, ub[rootx]);
			}
			else {
				ub[rooty] += a[now].w;
				if (rootx != rooty) {
					ub[rooty] += ub[rootx];
					ub[rootx] = 0;
				}
				maxx = max(maxx, ub[rooty]);
			}
			++now;
		}
		ans[q[i].num] = maxx;
	}
	for (int i = 1; i <= k; ++i) write(ans[i]), puts("");
	fclose(stdin), fclose(stdout);
	return 0;
}