文章目录
- title
- solution
- code
title
solution
一棵nnn个点的树有n−1n-1n−1条边。
一般的,对于森林而言则有点数-边数=树的个数
那么我们将无向图随便生成一个生成树,形成森林。
询问的连通块的个数就变为求森林的树的个数,即森林的点数 n −-− 森林的边数 === 连通块的个数。
然后就可以用LCTLCTLCT动态维护生成树了,将询问的 rrr 从小到大排序,主席树维护每条边是否在当前的生成树森林中出现。
在第 rrr 个版本的主席树中查 [l,r][l,r][l,r] 这段区间有多条边存在,这就是生成树森林的总边数。
LCTLCTLCT不好维护边权,建虚点连边即可。
对于[l,r][l,r][l,r]包含的边,假设就用先后输入的时间决定先后。
对于边iii,加入后若不能形成环,则直接加入;若形成环了,删掉环上最早的边
这样越往后的边越有可能存在于[l,r][l,r][l,r],rrr是递增的嘛。
所以正确性也是可以保障的。
⚡:有自环哦!
code
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 400005
#define inf 0x7f7f7f7f
struct node {int u, v;
}edge[maxn];struct LCT {int f, rev, val, minn;int son[2];
}tree[maxn];
int st[maxn];bool notroot( int x ) {return tree[tree[x].f].son[0] == x || tree[tree[x].f].son[1] == x;
}void update( int x ) {tree[x].minn = min( tree[x].val, min( tree[tree[x].son[0]].minn, tree[tree[x].son[1]].minn ) );
}void reverse( int x ) {swap( tree[x].son[0], tree[x].son[1] );tree[x].rev ^= 1;
} void pushdown( int x ) {if( ! tree[x].rev ) return;reverse( tree[x].son[0] );reverse( tree[x].son[1] );tree[x].rev = 0;
}void rotate(int x) { int fa = tree[x].f, Gfa = tree[fa].f;int k = tree[fa].son[1] == x;if( notroot( fa ) )tree[Gfa].son[tree[Gfa].son[1] == fa] = x;tree[x].f = Gfa;tree[fa].son[k] = tree[x].son[k ^ 1];if( tree[x].son[k ^ 1] )tree[tree[x].son[k ^ 1]].f = fa;tree[x].son[k ^ 1] = fa;tree[fa].f = x;update( fa );update( x );
}void splay( int x ) {int top = 0, y = x;st[++ top] = y;while( notroot( y ) ) st[++ top] = y = tree[y].f;while( top ) pushdown( st[top --] );while( notroot( x ) ) {int fa = tree[x].f, Gfa = tree[fa].f;if( notroot( fa ) ) ( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate( x ) : rotate( fa );rotate( x );}update( x );
}void access( int x ) {for( int son = 0;x;son = x, x = tree[x].f ) {splay( x );tree[x].son[1] = son;update( x );}
}void makeroot( int x ) {access( x );splay( x );swap( tree[x].son[0], tree[x].son[1] );tree[x].rev ^= 1;
}void split( int x, int y ) {makeroot( x );access( y );splay( y );
}int findroot( int x ) {access( x );splay( x );while( tree[x].son[0] ) x = tree[x].son[0];splay( x );return x;
}void link( int x, int y ) {split( x, y );tree[x].f = y;
}void cut( int x, int y ) {split( x, y );tree[x].f = tree[y].son[0] = 0;
}int ask( int x, int y ) {split( x, y );return tree[y].minn;
}struct SegmentTree {int cnt, l, r;
}seg[maxn * 20];
int tot;
int rt[maxn];void modify( int &num, int l, int r, int pos, int v ) {int x = ++ tot;seg[x].l = seg[num].l, seg[x].r = seg[num].r, seg[x].cnt = seg[num].cnt + v, num = x;if( l == r ) return;int mid = ( l + r ) >> 1;if( pos <= mid ) modify( seg[num].l, l, mid, pos, v );else modify( seg[num].r, mid + 1, r, pos, v );
}int query( int num, int l, int r, int L, int R ) {if( L <= l && r <= R ) return seg[num].cnt;int mid = ( l + r ) >> 1, ans = 0;if( L <= mid ) ans += query( seg[num].l, l, mid, L, R );if( mid < R ) ans += query( seg[num].r, mid + 1, r, L, R );return ans;
}int n, m, Q, t, last;void get( int &l, int &r ) {if( t ) l = ( l + last ) % m + 1, r = ( r + last ) % m + 1; if( l > r ) swap( l, r );
}int main() {scanf( "%d %d %d %d", &n, &m, &Q, &t );for( int i = 0;i <= n;i ++ ) tree[i].val = tree[i].minn = inf;for( int i = 1;i <= m;i ++ ) {scanf( "%d %d", &edge[i].u, &edge[i].v );tree[i + n].val = tree[i + n].minn = i;}for( int i = 1;i <= m;i ++ ) {int u = edge[i].u, v = edge[i].v;rt[i] = rt[i - 1];if( u == v ) continue;modify( rt[i], 1, m, i, 1 );if( findroot( u ) == findroot( v ) ) {int pos = ask( u, v );cut( edge[pos].u, pos + n );cut( edge[pos].v, pos + n );modify( rt[i], 1, m, pos, -1 );}link( u, i + n );link( v, i + n );}while( Q -- ) {int l, r;scanf( "%d %d", &l, &r );get( l, r );last = n - query( rt[r], 1, m, l, r );printf( "%d\n", last );}return 0;
}