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|ein [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 ", lastans = CMT::solve(L, R)); } }
@Author: YouSiki