P4185 [USACO18JAN]MooTube

这个题想了我2天,最后实在忍不住瞟了一眼题解,看到并查集三个字,一下就明白了。 还是利用离线 + 答案单调性的思想。不难发现,假如询问的$k$单调递减,那么答案一定单调递增。 所以考虑离线。将询问排序以后,每次将满足条件的边(即满足$k leq val$的边)连上,答案就是点所在连通块的大小。
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 20;
inline int read()
{
	int x = 0; char ch = getchar(); bool f = false;
	while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
	return f ? -x : x;
}

int N, Q;

struct edge
{
	int from, to, cost;
	inline bool operator <(const edge &rhs) const{
		return cost > rhs.cost;
	}
};vector<edge> g;
struct state
{
	int k, v, idx, res;
}ask[MAXN];

inline bool cmp1(state &lhs, state &rhs){
	return lhs.k > rhs.k; 
}
inline bool cmp2(state &lhs, state &rhs){
	return lhs.idx < rhs.idx;
}

namespace ufs
{
	int fa[MAXN], siz[MAXN];

	void init(){for(int i = 1; i <= N; i++) fa[i] = i, siz[i] = 1;}
	int findfa(int x){return fa[x] == x ? x : fa[x] = findfa(fa[x]);}
	void unite(int x, int y){x = findfa(x), y = findfa(y), siz[y] += siz[x], fa[x] = y;}
}

int main()
{
	cin>>N>>Q;
	for(int i = 1; i < N; i++){
		int u = read(), v = read(), c = read();
		g.push_back((edge){u, v, c});
	}
	for(int i = 1; i <= Q; i++){
		int k = read(), v = read();
		ask[i] = (state){k, v, i};
	}

	sort(ask + 1, ask + Q + 1, cmp1);
	sort(g.begin(), g.end());

	ufs::init(); vector<edge>::iterator it = g.begin();

	for(int i = 1; i <= Q; i++){
		while(it != g.end() && it->cost >= ask[i].k) ufs::unite(it->from, it->to), ++it;
		ask[i].res = ufs::siz[ufs::findfa(ask[i].v)];
	}
	
	sort(ask + 1, ask + Q + 1, cmp2);
	for(int i = 1; i <= Q; i++) printf("%d
", ask[i].res - 1);
	return 0;
}

原文地址:https://www.cnblogs.com/wsmrxc/p/9432848.html