题意:
初始有n个点,m次操作。每次操作加一条边或者询问两个点第一次连通的时刻(若不连通输出-1)。
题解:
用并查集维护每个点所在连通块的根。对于每次加边,暴力的更新新的根。
每次将2个块合并时,将小的块并向大的块。这么合并使得每个点的根最多更新log2n次,并储存每次更新信息(更新时刻以及新的根)。
对于每一次询问,二分两个点第一次连通的时刻。对于每一个二分的时刻,求的是两点的根是否相同。
由于存储过了每个点根的更新信息,所以再用二分求出他这个时刻的根。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+10; int t; int n, m; int u, v, k; int f[N], num[N]; vector<pair<int, int> > g[N]; vector<int> c[N]; bool check(int x) { int l = 0, r = g[u].size()-1; while(l <= r) { int mid = l+r>>1; if(g[u][mid].first <= x) l = mid+1; else r = mid-1; } int p1 = g[u][r].second; l = 0, r = g[v].size()-1; while(l <= r) { int mid = l+r>>1; if(g[v][mid].first <= x) l = mid+1; else r = mid-1; } int p2 = g[v][r].second; if(p1==p2) return 1; return 0; } int main() { scanf("%d", &t); while(t--) { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) { num[i] = 1; f[i] = i; c[i].clear(); g[i].clear(); c[i].push_back(i); g[i].push_back(make_pair(0, i)); } for(int i = 1; i <= m; i++) { scanf("%d%d%d", &k, &u, &v); if(k&1) { u = f[u]; v = f[v]; if(u!=v) { if(num[u]>num[v]) swap(u, v); for(int j = 0; j < num[u]; j++) { c[v].push_back(c[u][j]); f[c[u][j]] = v; g[c[u][j]].push_back(make_pair(i, v)); } num[v] += num[u]; num[u] = 0; c[u].clear(); } } else { int l = 0, r = i-1; while(l<=r) { int mid = l+r>>1; if(check(mid)) r = mid-1; else l = mid+1; } if(check(r+1)) printf("%d\n", r+1); else puts("-1"); } } } }
原文地址:https://www.cnblogs.com/Pneuis/p/9130170.html
时间: 2024-10-21 03:20:02