<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"><span style="white-space:pre"> </span>好久沒有寫動態樹了,都快忘了,今天來複習一下</span>
題意:給定N個點M條邊的圖,詢問選第L到R條邊時該圖的聯通塊個數。
我就直接講做法了:以每條邊的編號爲每條邊的邊權,然後用LCT維護最大生成樹,對於當前的一條邊x->y,如果x和y不聯通,那麼把x和y加入生成樹,如果x和y聯通,那麼在x到y的路徑上選取一條權值最小的邊,砍了,然後連接x和y,此時記錄每條邊彈出的邊的編號(記爲num),如果不連通就爲0,如果是自環就忽略。然後在詢問的時候,查詢L到R之間小於L的num的個數,這個用樹套樹或者用可持久化線段樹維護一下就好了,然後n - ans就是每組詢問的答案,至於爲什麼是這麼做,請讀者自行思考,這應該是很簡單的。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXX = 400005;
int x, y, i, j, k, n, m, qq;
int lc[MAXX], rc[MAXX], fa[MAXX], sum[MAXX], sum1[MAXX], cnt[MAXX], key[MAXX], num[MAXX], next[2][MAXX];
int ls[MAXX << 4], rs[MAXX << 4], sum2[MAXX << 4], len = 0, ans, root[MAXX], ty;
int q[MAXX];
inline int get()
{
char c;
while ((c = getchar()) < 48 || c > 57);
int res = c - 48;
while ((c = getchar()) >= 48 && c <= 57)
res = res * 10 + c - 48;
return res;
}
inline int isroot(int x)
{
return ((lc[fa[x]] == x) || (rc[fa[x]] == x));
}
inline void rev(int x)
{
cnt[x] ^= 1;
swap(lc[x], rc[x]);
}
inline void putdown(int x)
{
if (cnt[x])
{
cnt[x] = 0;
if (lc[x]) rev(lc[x]);
if (rc[x]) rev(rc[x]);
}
}
inline void pushup(int x)
{
sum1[x] = x;
sum[x] = key[x];
if (lc[x] && sum[lc[x]] < sum[x]) sum[x] = sum[lc[x]], sum1[x] = sum1[lc[x]];
if (rc[x] && sum[rc[x]] < sum[x]) sum[x] = sum[rc[x]], sum1[x] = sum1[rc[x]];
}
inline void turn(int x)
{
int y = fa[x], z = fa[y], b = 0;
if (x == lc[y]) b = rc[x];
else b = lc[x];
if (b) fa[b] = y;
fa[x] = z; fa[y] = x;
if (z)
if (lc[z] == y) lc[z] = x;
else if (rc[z] == y) rc[z] = x;
if (lc[y] == x) rc[x] = y, lc[y] = b;
else lc[x] = y, rc[y] = b;
pushup(y);
}
inline void splay(int x)
{
int len = 0, i;
for(i = x; isroot(i); i = fa[i])
q[++len] = i;
q[++len] = i;
while (len) putdown(q[len--]);
while (isroot(x))
{
if (isroot(fa[x]))
if ((lc[fa[x]] == x) == (lc[fa[fa[x]]] == fa[x])) turn(fa[x]);
else turn(x);
turn(x);
}
pushup(x);
}
inline void lct_access(int x)
{
int sb = 0;
while (x)
{
splay(x);
rc[x] = sb;
sb = x;
x = fa[x];
}
}
inline void makeroot(int x)
{
lct_access(x);
splay(x);
rev(x);
}
inline void LINK(int x, int y)
{
makeroot(x); fa[x] = y;
}
inline void lct_cut(int x, int y)
{
makeroot(x);
lct_access(y); splay(y);
fa[x] = lc[y] = 0;
pushup(y);
}
inline int check(int x, int y)
{
int xx = x, yy = y;
lct_access(xx); splay(xx);
while (lc[xx]) xx = lc[xx];
lct_access(yy); splay(yy);
while (lc[yy]) yy = lc[yy];
return (xx == yy);
}
inline void split(int x, int y)
{
makeroot(y);
lct_access(x);
splay(x);
}
inline void insert(int &k, const int &y, const int &p, const int &q, const int &w)
{
k = ++len;
sum2[k] = sum2[y];
ls[k] = ls[y];
rs[k] = rs[y];
sum2[k]++;
if (p == q) return;
int mid = (p + q) >> 1;
if (mid >= w) insert(ls[k], ls[y], p, mid, w);
else insert(rs[k], rs[y], mid + 1, q, w);
}
inline void find(const int &x, const int &y, const int &p, const int &q, const int &l, const int &r)
{
if (p >= l && q <= r)
{
ans += sum2[x] - sum2[y];
return;
}
int mid = (p + q) >> 1;
if (mid >= l) find(ls[x], ls[y], p, mid, l, r);
if (mid < r) find(rs[x], rs[y], mid + 1, q, l, r);
}
int main()
{
cin >> n >> m >> qq >> ty;
for(i = 1; i <= n; i ++)
key[i] = m + 1;
for(i = 1; i <= m; i ++)
{
x = get(); y = get();
key[i + n] = i;
next[0][i + n] = x;
next[1][i + n] = y;
if (x == y) {num[i] = m + 1; continue;}
if (!check(x, y)) LINK(x, i + n), LINK(i + n, y);
else{
split(x, y);
num[i] = sum[x];
k = sum1[x];
lct_cut(next[0][k], k);
lct_cut(next[1][k], k);
LINK(x, i + n);
LINK(i + n, y);
}
}
for(i = 1; i <= m; i ++)
insert(root[i], root[i - 1], 0, m + 1, num[i]);
m++;
for(i = 1; i <= qq; i ++)
{
x = get(); y = get();
if (ty) x ^= ans, y ^= ans;
if (x > y) swap(x, y);
ans = 0;
find(root[y], root[x - 1], 0, m, 0, x - 1);
printf("%d\n", ans = n - ans);
}
return 0;
}