bzoj3514:Codechef MARCH14 GERALD07加强版 关于一类LCT维护动态图的连通性问题

3514: Codechef MARCH14 GERALD07加强版

Time Limit: 60 Sec   Memory Limit: 256 MB
Submit: 1900   Solved: 721
[ 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

Sample Output

2
1
3
1

HINT

对于100%的数据,1≤N、M、K≤200,000。


2016.2.26提高时限至60s

Source


这是一道关于LCT维护动态图的连通性问题。

先说做法:

1.先将每条边加入图中,然后如果这条边所在两个点已经构成了环,那么把这个环中最早加入的一条边,并把这条边记录为当前边的前驱。early[i]=j

2.那么对于l~r区间的每天边,我们需要的答案就是n-([early[i]<l==1](l<=i<=r))也及n-(l~r中pre小于等于l的边的个数)

About正确性:如果要是early[i]<l说明这条边一定连接了两个连通块,因此这条边减少了一个连同块,对答案的贡献是-1。否则它对答案没有贡献。

维护?:对于1.可以用LCT,对于一条边记其边权是其加入时间戳。然后边权转点权,用LCT维护最小值节点即可。对于2.可以用主席树,查询区间小于某个数的个数即可。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define maxn 440000
#define maxt 4400000
#define ls ch[p][0]
#define rs ch[p][1]
#define inf 0x3f3f3f3f
using namespace std;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 - '0' + ch; ch = getchar();}
    return x * f;
}
 
int ch[maxn][2], fa[maxn], mn[maxn], val[maxn];
bool rev[maxn];
int root[maxn], sum[maxt], lc[maxt], rc[maxt], sz;
int n, m, Q, type, top, st[maxn], early[maxn], eu[maxn], ev[maxn];
bool wh(int p) {return ch[fa[p]][1] == p;}
bool Isroot(int p) {return ch[fa[p]][0] != p && ch[fa[p]][1] != p;} 
void update(int p) {
    mn[p] = p;
    if(val[mn[p]] > val[mn[ls]]) mn[p] = mn[ls];
    if(val[mn[p]] > val[mn[rs]]) mn[p] = mn[rs];
}
void push_down(int p) {
    if(rev[p]) {
        rev[ls] ^= 1; rev[rs] ^= 1;
        rev[p] ^= 1; swap(ls, rs);
    }
}
 
void push_up(int p) {
    st[top = 1] = p;
    for(int i = p; !Isroot(i); i = fa[i]) st[++top] = fa[i];
    for(int i = top; i; --i) push_down(st[i]);
}
  
void Rotate(int p) {
    int f = fa[p], g = fa[f], c = wh(p);
    if(!Isroot(f)) ch[g][wh(f)] = p; fa[p] = g;
    ch[f][c] = ch[p][c ^ 1]; if(ch[f][c]) fa[ch[f][c]] = f;
    ch[p][c ^ 1] = f; fa[f] = p;
    update(f);
}
  
void Splay(int p) {
    push_up(p);
    for(; !Isroot(p); Rotate(p)) 
        if(!Isroot(fa[p])) Rotate(wh(fa[p]) == wh(p) ? fa[p] : p);
    update(p);
}
  
void Access(int p) {
    for(int pre = 0; p; pre = p, p = fa[p]) {
        Splay(p);
        rs = pre;
        update(p);
    }
}
 
void makeroot(int p) {Access(p); Splay(p); rev[p] ^= 1;}
void Link(int p, int g) {
    makeroot(p);
    fa[p] = g;
}
 
void Cut(int p, int g) {
    makeroot(p); Access(g); Splay(g);
    fa[p] = ch[g][0] = 0;
}
 
int Query(int u, int v) {
    makeroot(u); Access(v); Splay(v);
    return mn[v];
}
int find(int p) {Access(p); Splay(p); while(ls) p = ls; return p;}
 
void add(int L, int R, int pre_p, int &cur_p, int val) {
    sum[cur_p = ++sz] = sum[pre_p] + 1;
    if(L == R) return;
    int mid = L + R >> 1;
    if(val <= mid) {
        rc[cur_p] = rc[pre_p];
        add(L, mid, lc[pre_p], lc[cur_p], val);
    }
    else {
        lc[cur_p] = lc[pre_p];
        add(mid + 1, R, rc[pre_p], rc[cur_p], val);
    }
}
 
void LCT() {
    int tot = n;
    for(int i = 1;i <= m; ++i) {
        int u = read(), v = read();
        eu[i] = u; ev[i] = v;
        if(u == v) {early[i] = i;continue;}
        if(find(u) == find(v)) {
            int t = Query(u, v); early[i] = val[t];
            Cut(eu[early[i]], t); Cut(ev[early[i]], t);
        }
        ++tot; mn[tot] = tot; val[tot] = i;
        Link(u, tot); Link(v, tot);    
    }
    //for(int i = 1; i <= m; ++i) cout<<early[i]<<" ";
    //cout<<endl;
    for(int i = 1;i <= m; ++i) add(0, m, root[i - 1], root[i], early[i]);
}
 
int query(int L, int R, int lt, int rt, int val) {
    if(R == val) return sum[rt] - sum[lt];
    int mid = L + R >> 1;
    if(val <= mid) return query(L, mid, lc[lt], lc[rt], val);
    else return sum[lc[rt]] - sum[lc[lt]] + query(mid + 1, R, rc[lt], rc[rt], val); 
}
 
void PST() {
    int lastans = 0;
    while(Q--) {
        int l = read(), r = read();
        if(type) l ^= lastans, r ^= lastans;
        lastans = n - query(0, m, root[l - 1], root[r], l - 1);
        printf("%d\n", lastans);
    }
}
  
int main()
{
    n = read(); m = read(); Q = read(); type = read(); val[0] = inf;
    for(int i = 1;i <= n; ++i) mn[i] = i, val[i] = inf;
    LCT(); PST();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值