3514: Codechef MARCH14 GERALD07加强版
Time Limit: 60 Sec Memory Limit: 256 MBSubmit: 2162 Solved: 828
[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
分析:挺好的一道题.
知道了如何利用点的信息来求连通块的个数这道题就好做了. 那么需要维护哪些信息? 怎么求呢?
按照编号一条一条边地加,如果当前加的这条边i的两个端点已经连通了,则找到这两个点的路径上编号最小的边j. 用i替代它,并令id[i] = j. 要求[L,R]的连通块的数量,就是n - [L,R]中有多少条边的id<L.
至于为什么,这个要脑补一下. 一开始是有n个点组成了n个连通块,如果一条边的id ≥ L,那么就会形成一个环,连通块的数量不变,如果id < L,那么就会将连通块连起来,合并成一个连通块,连通块的数量减少1.非常神奇.
至于怎么求id,这就是LCT的经典应用了,类似于bzoj2594,但是要注意:bzoj2594每次是求最大的边,这道题中是求最小的边,符号要变过来,一开始val要初始化为inf.
知道了id后,问题就变成了每次统计区间[L,R]中有多少个id[ai] < L. 统计有多少个,满足区间可减性. 统计的数与权值id有关,能想到什么?主席树!
求连通块数量的这么一种方法要记住了.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 400010; int n,m,Q,type,lastans,f[maxn],id[maxn],val[maxn],cnt,root[maxn]; int fa[maxn],son[maxn][2],rev[maxn],sta[maxn],maxx[maxn],tot; struct node { int x,y; }e[maxn]; struct node2 { int num,lson,rson; }tr[maxn * 20]; int find(int x) { if (x == f[x]) return x; return f[x] = find(f[x]); } bool is_root(int x) { return son[fa[x]][0] != x && son[fa[x]][1] != x; } bool get(int x) { return son[fa[x]][1] == x; } void pushdown(int x) { if (rev[x]) { rev[son[x][0]] ^= 1; rev[son[x][1]] ^= 1; rev[x] = 0; swap(son[x][0],son[x][1]); } } void pushup(int x) { maxx[x] = x; if (son[x][0]) if (val[maxx[son[x][0]]] < val[maxx[x]]) maxx[x] = maxx[son[x][0]]; if (son[x][1]) if (val[maxx[son[x][1]]] < val[maxx[x]]) maxx[x] = maxx[son[x][1]]; } void turn(int x) { int y = fa[x]; int z = fa[y]; int temp = get(x); if (!is_root(y)) son[z][son[z][1] == y] = x; fa[x] = z; son[y][temp] = son[x][temp ^ 1]; fa[son[y][temp]] = y; son[x][temp ^ 1] = y; fa[y] = x; pushup(y); pushup(x); } void splay(int x) { int top = 0; sta[++top] = x; for (int y = x; !is_root(y); y = fa[y]) sta[++top] = fa[y]; for (int i = top; i >= 1; i--) pushdown(sta[i]); for (int temp; !is_root(x); turn(x)) { if (!is_root(temp = fa[x])) { if (get(temp) == get(x)) turn(temp); else turn(x); } } } void Access(int x) { int t = 0; for (; x; t = x,x = fa[x]) { splay(x); son[x][1] = t; pushup(x); } } void Reverse(int x) { Access(x); splay(x); rev[x] ^= 1; } void Cut(int x,int y) { Reverse(x); Access(y); splay(y); fa[x] = son[y][0] = 0; } void Link(int x,int y) { Reverse(x); fa[x] = y; splay(x); } int query(int x,int y) { Reverse(x); Access(y); splay(y); return maxx[y]; } void insert(int l,int r,int x,int &y,int v) { tr[y = ++tot] = tr[x]; tr[y].num++; if (l == r) return; int mid = (l + r) >> 1; if (v <= mid) insert(l,mid,tr[x].lson,tr[y].lson,v); else insert(mid + 1,r,tr[x].rson,tr[y].rson,v); } int Query(int l,int r,int x,int y,int v) { if (r == v) return tr[y].num - tr[x].num; int mid = (l + r) >> 1; if (v <= mid) return Query(l,mid,tr[x].lson,tr[y].lson,v); else return tr[tr[y].lson].num - tr[tr[x].lson].num + Query(mid + 1,r,tr[x].rson,tr[y].rson,v); } int main() { scanf("%d%d%d%d",&n,&m,&Q,&type); for (int i = 1; i <= n; i++) f[i] = i; memset(val,127/3,sizeof(val)); for (int i = 1 + n; i <= m + n; i++) val[i] = i - n; for (int i = 1; i <= m; i++) { int x,y; scanf("%d%d",&x,&y); e[i].x = x; e[i].y = y; if (x == y) { id[i] = i; continue; } int fx = find(x),fy = find(y); if (fx != fy) { Link(x,i + n); Link(i + n,y); f[fx] = fy; id[i] = 0; } else { int temp = query(x,y); int temp2 = val[temp]; Cut(e[temp2].x,temp); Cut(temp,e[temp2].y); Link(x,i + n); Link(i + n,y); id[i] = temp2; } } for (int i = 1; i <= m; i++) insert(0,m,root[i - 1],root[i],id[i]); for (int i = 1; i <= Q; i++) { int l,r; scanf("%d%d",&l,&r); if (type == 1) { l ^= lastans; r ^= lastans; } printf("%d ",lastans = (n - Query(0,m,root[l - 1],root[r],l - 1))); } return 0; }