1 solutions

  • -1
    @ 2024-2-23 14:21:24

    类似最小生成树,考虑从小到大加边,如果加完一条边之后,u,vu,v 这两个点所在的 连通块,只需要再加一条边就可以连通,那么这条边就是第二小的,也就是答案。 接着考虑如何维护这一过程。对于一个连通块 uu ,记与这个连通块有边相连的点为 N(u)N(u) 。考虑启发式合并,对于两个连通块 u,vu,v 合并后,那么可能 N(u)N(u) 中的点和 vv 有 边相连了,N(v)N(v) 中的点和 uu 有边相连了。如果直接遍历 N(u) 和 N(v)N(v) 中的点,那么时间复杂度是显然不对的。 我们可以做的是,遍历 N(u)N(u) 的所有点,然后查询是不是 vv 相连,然后遍历 uu 里面的所有询问,看是不是在 N(v)N(v) 之中即可。这样只需要遍历其中一部分的信息,所以 可以启发式合并。每次我们按 N(v)N(v) 大小加询问个数比较,然后遍历小的部分,启发式合并即可。合并完之后,更新一下合并之后的连通块的邻居和询问信息。 时间复杂度是 O(nlogn)O(nlogn) 的。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 400005
    
    #define pb push_back
    int fa[N];
    struct node {
    	int x, y, z;
    } b[N];
    int ans[N], n, m, Q;
    set<array<int, 2>> q[N];
    set<int> e[N];
    
    int get(int x) {
    	if (fa[x] == x)
    		return x;
    	return fa[x] = get(fa[x]);
    }
    
    inline bool cmp(node x, node y) {
    	return x.z < y.z;
    }
    
    void combine(int x, int y, int val) {
    	for (auto u : e[x]) {
    		while (1) {
    			auto it = q[y].lower_bound({u, -1});
    			if (it == q[y].end() || (*it)[0] != u)
    				break;
    			int id = (*it)[1];
    			ans[id] = val;
    			assert(q[y].count({u, id}));
    			assert(q[u].count({y, id}));
    			q[y].erase(it);
    			q[u].erase({y, id});
    		}
    	}
    	vector<array<int, 2>> delq;
    	for (auto u : q[x]) {
    		if (e[y].count(u[0])) {
    			ans[u[1]] = val;
    			q[u[0]].erase({x, u[1]});
    			delq.pb(u);
    		}
    	}
    	for (auto u : delq)
    		q[x].erase(u);
    	fa[x] = y;
    	for (auto v : e[x]) {
    		assert(e[v].count(x));
    		e[v].erase(x);
    		if (v != y) {
    			e[v].insert(y);
    			e[y].insert(v);
    		}
    	}
    	e[x].clear();
    	for (auto v : q[x]) {
    		assert(v[0] != y);
    		assert(q[v[0]].count({x, v[1]}));
    		q[v[0]].erase({x, v[1]});
    		q[v[0]].insert({y, v[1]});
    		q[y].insert({v[0], v[1]});
    	}
    	q[x].clear();
    }
    
    int main() {
    	freopen("path.in", "r", stdin);
    	freopen("path.out", "w", stdout);
    	scanf("%d%d%d", &n, &m, &Q);
    	for (int i = 1; i <= n; i++)
    		e[i].clear(), q[i].clear();
    	for (int i = 1; i <= m; i++) {
    		scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].z);
    		e[b[i].x].insert(b[i].y);
    		e[b[i].y].insert(b[i].x);
    	}
    	for (int i = 1; i <= n; i++)
    		fa[i] = i;
    	sort(b + 1, b + 1 + m, cmp);
    	for (int i = 1; i <= Q; i++) {
    		ans[i] = 0;
    		int x, y;
    		scanf("%d%d", &x, &y);
    		if (e[x].count(y)) {
    			ans[i] = 1;
    			continue;
    		}
    		q[x].insert({y, i});
    		q[y].insert({x, i});
    	}
    	for (int i = 1; i <= m; i++) {
    		b[i].x = get(b[i].x), b[i].y = get(b[i].y);
    		if (b[i].x == b[i].y)
    			continue;
    		if (q[b[i].x].size() + e[b[i].x].size() > q[b[i].y].size() + e[b[i].y].size())
    			swap(b[i].x, b[i].y);
    		combine(b[i].x, b[i].y, b[i].z + 1);
    	}
    	for (int i = 1; i <= Q; i++)
    		printf("%d\n", ans[i] - 1);
    }
    
    • 1

    Information

    ID
    315
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    10
    Tags
    # Submissions
    11
    Accepted
    0
    Uploaded By