3514: Codechef MARCH14 GERALD07加强版
Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 1356 Solved: 514
[Submit][Status][Discuss]
Description
N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
Input
第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。
接下来M行,代表图中的每条边。
接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。
Output
K行每行一个整数代表该组询问的联通块个数。
Sample Input
3 5 4 0
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
Sample Output
2
1
3
1
1
3
1
HINT
对于100%的数据,1≤N、M、K≤200,000。
2016.2.26提高时限至60s
Source
如果是离线的话,我们可以LCT+莫队什么的乱搞是吧,但是在线就……
不过还是有一个很喵的做法——
我们用LCT维护一棵生成树,当加入一条边$i$的时候($i$是其编号),其连接的两个点可能已经联通,加入$i$之后会形成一个环,我们弹掉这个环上编号最小的边(也就是加入最早的边),并记录其编号为$ntr_{i}$。特殊的,如果$i$没有弹掉任何边,我们记$ntr_{i}=0$。
对于一个询问$[L,R]$(表示我们只保留$e|e\in [L,R]$),答案就是$n-\sum_{i=L}^{R}{(ntr_{i}\lt L)}$。这个就是主席树了。
#include <cstdio>
inline int nextChar(void) {
static const int siz = 1 << 20;
static char buf[siz];
static char *hd = buf + siz;
static char *tl = buf + siz;
if (hd == tl)
fread(hd = buf, 1, siz, stdin);
return int(*hd++);
}
inline int nextInt(void) {
register int ret = 0;
register int neg = false;
register int bit = nextChar();
for (; bit < 48; bit = nextChar())
if (bit == '-')neg ^= true;
for (; bit > 47; bit = nextChar())
ret = ret * 10 + bit - '0';
return neg ? -ret : ret;
}
template <class T>
inline void Swap(T &a, T &b) {
T c;
c = a;
a = b;
b = c;
}
template <class T>
inline T Max(const T &a, const T &b) {
return a > b ? a : b;
}
template <class T>
inline T Min(const T &a, const T &b) {
return a < b ? a : b;
}
const int mxn = 400005;
const int inf = 1000000007;
int n, m, q, e, ntr[mxn];
struct edge {
int x, y;
}E[mxn];
namespace LCT {
int top = 0;
int stk[mxn];
int val[mxn];
int rev[mxn];
int min[mxn];
int fat[mxn];
int son[mxn][2];
inline bool isroot(int t) {
int f = fat[t];
if (!f)return true;
if (son[f][0] == t)return false;
if (son[f][1] == t)return false;
return true;
}
inline void update(int t) {
min[t] = val[t];
if (son[t][0])min[t] = Min(min[t], min[son[t][0]]);
if (son[t][1])min[t] = Min(min[t], min[son[t][1]]);
}
inline void push(int t) {
rev[t] = 0;
Swap(son[t][0], son[t][1]);
if (son[t][0])rev[son[t][0]] ^= 1;
if (son[t][1])rev[son[t][1]] ^= 1;
}
inline void pushdown(int t) {
for (stk[++top] = t; t; )
stk[++top] = t = fat[t];
for (; top; --top)
if (rev[stk[top]])
push(stk[top]);
}
inline void connect(int t, int f, int s) {
if (t)fat[t] = f;
if (f)son[f][s] = t;
}
inline void rotate(int t) {
int f = fat[t];
int g = fat[f];
int s = son[f][1] == t;
connect(son[t][!s], f, s);
connect(f, t, !s);
fat[t] = g;
if (g && son[g][0] == f)son[g][0] = t;
if (g && son[g][1] == f)son[g][1] = t;
update(f);
update(t);
}
inline void splay(int t) {
pushdown(t);
while (!isroot(t)) {
int f = fat[t];
int g = fat[f];
if (isroot(f))
rotate(t);
else {
int a = f && son[f][1] == t;
int b = g && son[g][1] == f;
if (a == b)
rotate(f), rotate(t);
else
rotate(t), rotate(t);
}
}
}
inline void access(int t) {
for (int p = 0; t; p = t, t = fat[t])
splay(t), son[t][1] = p, update(t);
}
inline void makeroot(int t) {
access(t), splay(t), rev[t] ^= 1;
}
inline int find(int t) {
access(t), splay(t);
while (son[t][0])
t = son[t][0];
return t;
}
inline void link(int t, int f) {
makeroot(t), fat[t] = f;
}
inline void cut(int a, int b) {
makeroot(a), access(b), splay(b);
if (son[b][0] == a)
son[b][0] = fat[a] = 0, update(b);
}
inline void preworkNTR(void) {
for (int i = 1; i <= n; ++i)
val[i] = min[i] = inf;
for (int i = 1; i <= m; ++i) {
if (E[i].x == E[i].y) {
ntr[i] = m;
continue;
}
if (find(E[i].x) == find(E[i].y)) {
makeroot(E[i].x);
access(E[i].y);
splay(E[i].y);
ntr[i] = min[E[i].y];
cut(E[ntr[i]].x, ntr[i] + n);
cut(E[ntr[i]].y, ntr[i] + n);
}
val[i + n] = min[i + n] = i;
link(E[i].x, i + n);
link(E[i].y, i + n);
}
}
}
namespace CMT {
const int K = 25;
int tot = 0;
int sum[K * mxn];
int lsn[K * mxn];
int rsn[K * mxn];
int rot[K * mxn];
void insert(int &t, int p, int l, int r, int v) {
t = ++tot;
lsn[t] = lsn[p];
rsn[t] = rsn[p];
sum[t] = sum[p] + 1;
if (l == r)return;
int mid = (l + r) >> 1;
if (v <= mid)
insert(lsn[t], lsn[p], l, mid, v);
else
insert(rsn[t], rsn[p], mid + 1, r, v);
}
int query(int t, int l, int r, int x, int y) {
if (!t)return 0;
if (l == x && r == y)
return sum[t];
int mid = (l + r) >> 1;
if (y <= mid)
return query(lsn[t], l, mid, x, y);
else if (x > mid)
return query(rsn[t], mid + 1, r, x, y);
else
return query(lsn[t], l, mid, x, mid) + query(rsn[t], mid + 1, r, mid + 1, y);
}
inline void preworkNTR(void) {
for (int i = 1; i <= m; ++i)
insert(rot[i], rot[i - 1], 0, m, ntr[i]);
}
inline int solve(int l, int r) {
return n - query(rot[r], 0, m, 0, l - 1) + query(rot[l - 1], 0, m, 0, l - 1);
}
}
signed main(void) {
n = nextInt();
m = nextInt();
q = nextInt();
e = nextInt();
for (int i = 1; i <= m; ++i)
E[i].x = nextInt(),
E[i].y = nextInt();
LCT::preworkNTR();
CMT::preworkNTR();
int lastans = 0;
for (int i = 1; i <= q; ++i) {
int L = nextInt();
int R = nextInt();
if (e)
L ^= lastans,
R ^= lastans;
printf("%d\n", lastans = CMT::solve(L, R));
}
}
@Author: YouSiki