bzoj 3514: Codechef MARCH14 GERALD07加强版 lct+主席树

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 % 100\% 100%的数据, 1 ≤ N , M , K ≤ 200000 1≤N,M,K≤200000 1N,M,K200000

分析:
一种很暴力的想法就是把 [ l , r ] [l,r] [l,r]的生成树建出来。
我们可以把 [ 1 , r ] [1,r] [1,r]的生成树建出来,尽量使用编号大的边(可以理解为以编号为边权建最大生成树)。此时, [ l , r ] [l,r] [l,r]生成树的连通块数目就是 [ 1 , r ] [1,r] [1,r]生成树的连通块数目加上这棵树里面编号在 [ 1 , l − 1 ] [1,l-1] [1,l1]范围内的边数。
我们可以通过lct维护生成树维护 [ 1 , i ] [1,i] [1,i]的生成树,尽量使用后面的边替代前面的边,并记录下替换掉的边为编号,插入到一颗可持久化权值线段树中。
查询时就查询 t r e e [ r ] tree[r] tree[r] [ 1 , l − 1 ] [1,l-1] [1,l1]的边数,用 l − 1 l-1 l1去减掉这个值就是当前树里面编号在 [ 1 , l − 1 ] [1,l-1] [1,l1]范围内的边数。

代码:

/**************************************************************
    Problem: 3514
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:28928 ms
    Memory:202888 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
 
const int maxn=2e5+7;
const int inf=0x3f3f3f3f;
 
using namespace std;
 
int n,m,T,type,x,y,ans,cnt;
int from[maxn],to[maxn],root[maxn],num[maxn],val[maxn*2];
 
struct rec{
    int l,r,data;
}t[maxn*80];
 
struct LCT{
    struct node{
        int l,r,fa,data,id;
        bool rev;
    }t[maxn*2];
    bool isroot(int x)
    {
        return (x!=t[t[x].fa].l) && (x!=t[t[x].fa].r);
    }
    void updata(int x)
    {
        int l=t[x].l,r=t[x].r;
        if (t[l].data<t[r].data) t[x].data=t[l].data,t[x].id=t[l].id;
                            else t[x].data=t[r].data,t[x].id=t[r].id;
        if (val[x]<t[x].data) t[x].data=val[x],t[x].id=x;
    }
    void remove(int x)
    {
        if (!isroot(x)) remove(t[x].fa);
        if (t[x].rev)
        {
            t[x].rev^=1;
            swap(t[x].l,t[x].r);
            if (t[x].l) t[t[x].l].rev^=1;
            if (t[x].r) t[t[x].r].rev^=1;
        }
    }
    void rttr(int x)
    {
        int y=t[x].l;
        t[x].l=t[y].r;
        if (t[y].r) t[t[y].r].fa=x;
        if (x==t[t[x].fa].l) t[t[x].fa].l=y;
        else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
        t[y].fa=t[x].fa;
        t[x].fa=y;
        t[y].r=x;
        updata(x),updata(y);
    }
    void rttl(int x)
    {
        int y=t[x].r;
        t[x].r=t[y].l;
        if (t[y].l) t[t[y].l].fa=x;
        if (x==t[t[x].fa].l) t[t[x].fa].l=y;
        else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
        t[y].fa=t[x].fa;
        t[x].fa=y;
        t[y].l=x;
        updata(x),updata(y);
    }
    void splay(int x)
    {
        remove(x);
        while (!isroot(x))
        {
            int p=t[x].fa,g=t[p].fa;
            if (isroot(p))
            {
                if (x==t[p].l) rttr(p);
                          else rttl(p);
            }
            else
            {
                if (x==t[p].l)
                {
                    if (p==t[g].l) rttr(p),rttr(g);
                              else rttr(p),rttl(g);
                }
                else
                {
                    if (p==t[g].l) rttl(p),rttr(g);
                              else rttl(p),rttl(g);
                }
            }
        }
    }
    void access(int x)
    {
        int y=0;
        while (x)
        {
            splay(x);
            t[x].r=y;
            updata(x);
            y=x,x=t[x].fa;
        }
    }
    void makeroot(int x)
    {
        access(x);
        splay(x);
        t[x].rev^=1;
    }
    void link(int x,int y)
    {
        makeroot(x);
        splay(x);
        access(y);
        splay(y);
        t[x].fa=y;
    }
    void cut(int x,int y)
    {
        makeroot(x);
        access(y);
        splay(y);
        t[y].l=0,t[x].fa=0;
        updata(y);
    }
    int getroot(int x)
    {
        if (isroot(x)) return x;
        return getroot(t[x].fa);
    }
    bool check(int x,int y)
    {
        makeroot(x);
        access(y);
        splay(y);
        return getroot(x)==y;
    }
}lct;
 
void ins(int &p,int q,int l,int r,int x,int k)
{
    if (!p) p=++cnt;
    t[p].data=t[q].data+k;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,k);
           else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,k);
}
 
int getsum(int p,int l,int r,int x,int y)
{
    if (x>y) return 0;
    if ((l==x) && (r==y)) return t[p].data;
    int mid=(l+r)/2;
    if (y<=mid) return getsum(t[p].l,l,mid,x,y);
    else if (x>mid) return getsum(t[p].r,mid+1,r,x,y);
    return getsum(t[p].l,l,mid,x,mid)+getsum(t[p].r,mid+1,r,mid+1,y);
}
 
int main()
{
    scanf("%d%d%d%d",&n,&m,&T,&type);
    for (int i=1;i<=m;i++) scanf("%d%d",&from[i],&to[i]);
    for (int i=0;i<=n+m;i++) lct.t[i].data=val[i]=inf;
    num[0]=n;   
    for (int i=1;i<=m;i++)
    {
        num[i]=num[i-1];
        if (from[i]==to[i])
        {
            ins(root[i],root[i-1],1,m,i,1);
            continue;
        }
        if (lct.check(from[i],to[i]))
        {
            int d=lct.t[to[i]].id;
            lct.cut(from[d-n],d);
            lct.cut(d,to[d-n]);
            ins(root[i],root[i-1],1,m,d-n,1);
            num[i]++;
        }
        else root[i]=root[i-1];
        if (!lct.check(from[i],to[i]))
        {
            val[i+n]=i;
            lct.link(from[i],i+n);
            lct.link(i+n,to[i]);
            num[i]--;
        }
    }
    ans=0;
    for (int i=1;i<=T;i++)
    {
        scanf("%d%d",&x,&y);
        x^=ans*type,y^=ans*type;
        ans=num[y]+(x-1)-getsum(root[y],1,m,1,x-1);
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值