\(O(mk\alpha(n))\)
暴力,考慮對於每個詢問 \(l,r\),列舉 \(1\sim l-1,r+1\sim m\),並查集連邊即可。
1154 ms。
\(O(n(m+k\alpha(n)))\)
我們發現列舉 \(i\in [1,l),j\in (r,m]\) 太慢了。
考慮先預處理出並查集從 \(1\) 連邊到編號為 \(id\) 的邊的狀態 \(pre_{id}\),倒過來再處理出 \(suf_{id}\)。
詢問 \([l,r]\) 時只需要 \(O(n\alpha(n))\) 合併兩個並查集 \(pre_{l-1},suf_{r+1}\) 即可。
如何合併並查集 \(A,B\):對於每個點 \(i\),在以 \(A\) 為基礎的新並查集裡面合併 \(i\) 分別在 \(A\) 和 \(B\) 中所屬根的編號。
404 ms。
\(O(n^2+k)\)
注意到 \(pre\) 和 \(suf\) 其實只改變了不超過 \(n\) 次(每次合併一個連通塊)。
所以 \(l\) 可以找到小於 \(l\) 的離它最近的 \(pre\) 的變化位置 \(l'\),\(r\) 找到大於 \(r\) 的最近 \(suf\) 變化位置 \(r'\)。
答案就是合併 \(pre_{l'},suf_{r'}\) 後連通塊的個數。
注意到 \((l',r')\) 的組合只有 \(n^2\) 種。
於是可以預處理出變化位置,然後再預處理每種 \(l',r'\) 的組合,複雜度 \(O(n^2)\)。
總複雜度 \(O(n^2+k)\)。
124 ms。
\(O(m\sqrt k\alpha(n))\)
注意到是區間查詢,考慮莫隊。
注意到並查集不太好擴大區間(刪除邊),於是用回滾莫隊即可。
154 ms。
\(O(mk\alpha(n))\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505, M = 1e4 + 5;
int fa[N], sz[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
inline bool merge(int x, int y)
{
x = find(x), y = find(y);
if(x == y) return 0;
if(sz[x] > sz[y]) swap(x, y);
sz[y] += sz[x];
fa[x] = y;
return 1;
}
int u[M], v[M], n, m, k;
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i ++) cin >> u[i] >> v[i];
cin >> k;
while(k --)
{
int l, r; cin >> l >> r;
int cnt = n;
for(int i = 1; i <= n; i ++) fa[i] = i, sz[i] = 1;
for(int i = 1; i < l; i ++)
cnt -= merge(u[i], v[i]);
for(int i = r + 1; i <= m; i ++)
cnt -= merge(u[i], v[i]);
cout << cnt << "\n";
}
return 0;
}
\(O(n(m+k\alpha(n)))\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505, M = 1e4 + 5;
struct dsu {
int fa[N], sz[N], cnt;
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
inline void merge(int x, int y)
{
x = find(x), y = find(y);
if(x == y) return;
if(sz[x] > sz[y]) swap(x, y);
sz[y] += sz[x];
fa[x] = y;
cnt --;
} } pre[M], suf[M];
int u[M], v[M], n, m, k;
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i ++)
cin >> u[i] >> v[i];
for(int i = 1; i <= n; i ++)
{
pre[0].fa[i] = i, pre[0].sz[i] = 1, pre[0].cnt = n;
suf[m + 1].fa[i] = i, suf[m + 1].sz[i] = 1, suf[m + 1].cnt = n;
}
for(int i = 1; i <= m; i ++)
{
pre[i] = pre[i - 1];
pre[i].merge(u[i], v[i]);
}
for(int i = m; i >= 1; i --)
{
suf[i] = suf[i + 1];
suf[i].merge(u[i], v[i]);
}
cin >> k;
while(k --)
{
int l, r; cin >> l >> r;
dsu t = pre[l - 1];
for(int i = 1; i <= n; i ++)
t.merge(i, suf[r + 1].find(i));
cout << t.cnt << "\n";
}
return 0;
}
\(O(n^2+k)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505, M = 1e4 + 5;
struct dsu {
int fa[N], cnt;
int find(int x)
{
while(fa[x] != x)
{
x = fa[x] = fa[fa[x]];
}
return x;
}
inline bool merge(int x, int y)
{
x = find(x), y = find(y);
if(x == y) return 0;
fa[x] = y;
cnt --;
return 1;
} } p, s;
int u[M], v[M], n, m, k;
int pos[N], pos2[N], h, c;
int ans[N][N], ppos[M], ppos2[M];
bool vis[N];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i ++)
cin >> u[i] >> v[i];
p.cnt = n;
s.cnt = n;
for(int i = 1; i <= n; i ++)
{
p.fa[i] = i;
s.fa[i] = i;
}
dsu now;
ans[0][0] = n;
for(int i = m; i >= 1; i --)
{
if(s.merge(u[i], v[i]))
pos2[++h] = i, ans[0][h] = s.cnt;
ppos2[i] = h;
}
c = h; h = 0;
for(int i = 1; i <= c; i ++) vis[i] = 1;
for(int i = 1; i <= m; i ++)
{
if(!p.merge(u[i], v[i]))
{
ppos[i] = h;
continue;
}
now = p;
if(i) pos[++h] = i;
ppos[i] = h;
ans[h][0] = now.cnt;
for(int j = 1; pos2[j] >= i; j ++)
{
now.merge(u[pos2[j]], v[pos2[j]]);
ans[h][j] = now.cnt;
}
}
cin >> k;
while(k --)
{
int l, r; cin >> l >> r;
int p1 = ppos[l - 1], p2 = ppos2[r + 1];
cout << ans[p1][p2] << "\n";
}
return 0;
}
\(O(m\sqrt k\alpha(n))\)
#include<bits/stdc++.h>
#define blk(_X) (blk[_X])
using namespace std;
typedef long long ll;
#define int ll
const int M = 2e4 + 5, N = 505;
int n, m, k, sz;
int blk[M], ans[M];
struct node{int l, r, id;};
node q[M];
bool cmpq(node x, node y) {return blk(x.l) == blk(y.l) ? x.r > y.r : x.l < y.l;}
vector<pair<int, int>> logs;
struct dsu
{
int fa[N], cnt;
inline int find(int x)
{
while(fa[x] != x)
{
x = fa[x] = fa[fa[x]];
}
return x;
}
inline bool merge(int x, int y)
{
x = find(x), y = find(y);
if(x == y) return 0;
fa[x] = y;
cnt --;
return 1;
}
void init(int n)
{
for(int i = 1; i <= n; i ++) fa[i] = i;
cnt = n;
}
} t;
void init()
{
sz = m / sqrt(k) + 70;
for(int i = 1; i <= m + 1; i ++)
blk[i] = i / sz;
}
int u[M], v[M];
int upd(int c)
{
int x = t.find(u[c]), y = t.find(v[c]);
if(x == y) return 0;
t.fa[x] = y;
t.cnt --;
return 1;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i ++) cin >> u[i] >> v[i];
cin >> k;
for(int i = 1; i <= k; i ++) cin >> q[i].l >> q[i].r, q[i].l --, q[i].r ++, q[i].id = i;
init();
sort(q + 1, q + k + 1, cmpq);
int l = -1, r;
for(int i = 1; i <= k; i ++)
{
if(l < 0 || blk(l) != blk(q[i].l))
{
t.init(n);
for(int j = m; j >= q[i].r; j --) upd(j);
for(int j = 1; j <= blk(q[i].l) * sz; j ++) upd(j);
r = q[i].r, l = blk(q[i].l) * sz;
}
while(r > q[i].r) r --, upd(r);
int Ll = l;
dsu bak = t;
while(l < q[i].l) l ++, upd(l);
ans[q[i].id] = t.cnt;
l = Ll;
t = bak;
}
for(int i = 1; i <= k; i ++)
cout << ans[i] << "\n";
return 0;
}