P5385 [Cnoi2019]须臾幻境 / #3514. Codechef MARCH14 GERALD07加强版 [LCT + 主席树]
如果没有强制在线,那么可以树状数组+扫描线。
我们分析一下,如果有 \(x\) 条有作用的边,那么很显然是 \(n - x\) 个连通块。
如果它是第一条边,也就是上一条边是 0,那么显然可以加入我们的这个答案。
如果和它重复的那条边在 0 ~ l-1 那么很显然也可以加入答案对吧,因为你这条边是连上的了。
所以主席树一下就可以了。
两题的在线方式不同,注意一下。(
// by Isaunoya
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
struct io {
char buf[1 << 23 | 3], *s;
int f;
io() { f = 0, buf[fread(s = buf, 1, 1 << 23, stdin)] = '\n'; }
io& operator >> (int&x) {
for(x = f = 0; !isdigit(*s); ++s) f |= *s == '-';
while(isdigit(*s)) x = x * 10 + (*s++ ^ 48);
return x = f ? -x : x, *this;
}
};
const int maxn = 4e5 + 54;
int U[maxn], V[maxn], fuck[maxn];
struct lct {
int ch[maxn][2], fa[maxn], mn[maxn], val[maxn];
bool rev[maxn];
lct(){ memset(val, 0x3f, sizeof(val)); }
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
bool isroot(int x) { return ls(fa[x]) != x && rs(fa[x]) != x; }
bool getr(int x) { return rs(fa[x]) == x; }
void up(int x) {
mn[x] = x;
if(val[mn[x]] > val[mn[ls(x)]]) mn[x] = mn[ls(x)];
if(val[mn[x]] > val[mn[rs(x)]]) mn[x] = mn[rs(x)];
}
void pr(int x) { swap(ls(x), rs(x)), rev[x] ^= 1; }
void down(int x) {
if(! rev[x]) return;
if(ls(x)) pr(ls(x));
if(rs(x)) pr(rs(x));
rev[x] = 0;
}
void pushall(int x) {
if(! isroot(x)) pushall(fa[x]);
down(x);
}
void rtt(int x) {
int y = fa[x], z = fa[y], k = getr(x), w = ch[x][k ^ 1];
if(!isroot(y)) ch[z][getr(y)] = x;
fa[fa[fa[ch[ch[x][k ^ 1] = y][k] = w] = y] = x] = z, up(y), up(x);
}
void splay(int x) {
pushall(x);
while(!isroot(x)) {
int y = fa[x];
if(!isroot(y)) { rtt(getr(x) ^ getr(y) ? x : y); }
rtt(x);
}
}
int frt(int x) { acs(x); splay(x); while(ls(x)) down(x), x = ls(x), splay(x); return x; }
void acs(int x) { for(int tp = 0; x; x = fa[tp = x]) splay(x), rs(x) = tp, up(x); }
void mrt(int x) { acs(x), splay(x), pr(x); }
void link(int x, int y) { mrt(x); if(frt(y) != x) fa[x] = y; }
void cut(int x, int y) { mrt(x); if(frt(y) == x && fa[y] == x && !ls(y)) fa[y] = ls(x) = 0, up(x); }
int qry(int x, int y) { mrt(x); acs(y); splay(y); return mn[y]; }
} lct;
int ls[maxn << 6], rs[maxn << 6], sum[maxn << 6], rt[maxn], cnt = 0;
void upd(int &p, int pre, int l, int r, int v) {
sum[p = ++ cnt] = sum[pre] + 1;
ls[p] = ls[pre], rs[p] = rs[pre];
if(l == r) return;
int mid = l + r >> 1;
if(v <= mid)
upd(ls[p], ls[pre], l, mid, v);
else
upd(rs[p], rs[pre], mid + 1, r, v);
}
int qry(int ql, int qr , int a , int b , int l, int r) {
if(ql <= l && r <= qr) { return sum[b] - sum[a]; }
int mid = l + r >> 1, ans = 0 ;
if(ql <= mid)
ans += qry(ql, qr, ls[a], ls[b], l, mid);
if(qr > mid)
ans += qry(ql, qr, rs[a], rs[b], mid + 1, r);
return ans;
}
int n, m, q, type;
signed main() {
#ifdef LOCAL
freopen("testdata.in", "r", stdin);
#endif
io in;
in >> n >> m >> q >> type;
int tot = n;
for(int i = 1 ; i <= m ; i ++) {
int u, v;
in >> u >> v; U[i] = u; V[i] = v;
if(u == v) { fuck[i] = i; continue; }
if(lct.frt(u) == lct.frt(v)) {
int t = lct.qry(u, v); fuck[i] = lct.val[t];
lct.cut(U[fuck[i]], t); lct.cut(V[fuck[i]], t);
}
++ tot;
lct.mn[tot] = tot; lct.val[tot] = i;
lct.link(u, tot); lct.link(v, tot);
}
for(int i = 1 ; i <= m ; i ++) upd(rt[i], rt[i - 1], 0, m, fuck[i]);
int lasans = 0;
while(q --) {
int l, r; in >> l >> r;
if(type) l = (l + lasans) % m + 1,r = (r + lasans) % m + 1;
if(l > r) swap(l, r);
lasans = n - qry(0, l - 1, rt[l - 1], rt[r], 0, m);
cout << lasans << '\n';
}
return 0;
}