[CC]Chef and Graph Queries

2 篇文章 0 订阅
1 篇文章 0 订阅

题意简述

n 个点m个条边的无向图, q 组询问,每次询问只保留编号在[li,ri]的边时,图有多少联通块。

数据范围

1n,m,q2×105

思路

把所有询问离线后排序,按照右端点的边的序号递增的顺序处理。
边的序号即为它的权值,把边也看成点加入LCT,维护最大生成森林。
每次加入一个边(点),找到它形成的环上权值最小的一个点,删除之。
用树状数组维护在最大生成森林中的边。
每次查询用树状数组查询[L,R]区间。
复杂度 O((n+m+q)logn)

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 200010
#define INF 0x3f3f3f3f
struct Qe{
    int l,r,id;
    bool operator < (const Qe &n1) const
    {
        return r<n1.r;
    }
}Q[MAXN];
int T,n,m,q;
int x[MAXN],y[MAXN],ans[MAXN];
bool is_in[MAXN];
namespace LCT
{
    struct Node{
        Node *fa,*ch[2];
        bool rev;
        int data,mn,pos,id;
        bool chlr()
        {
            return fa->ch[1]==this;
        }
        bool is_rt()
        {
            return fa->ch[0]!=this&&fa->ch[1]!=this;
        }
        void modify_rev()
        {
            rev^=1;
            swap(ch[0],ch[1]);
        }
        void pushup()
        {
            mn=min(min(ch[0]->mn,ch[1]->mn),data);
            if (data<ch[0]->mn&&data<ch[1]->mn)
                pos=id;
            else if (ch[0]->mn<ch[1]->mn)
                pos=ch[0]->pos;
            else
                pos=ch[1]->pos;
        }
        void pushdown()
        {
            if (rev)
            {                                                                                                                               
                ch[0]->modify_rev();
                ch[1]->modify_rev();
                rev=0;
            }
        }
        Node();
        void Clear();
    }*null=new Node;

    Node::Node()
    {
        fa=ch[0]=ch[1]=null;
        rev=0;
        data=mn=INF;
        pos=-1;
    }
    void Node::Clear()
    {
        fa=ch[0]=ch[1]=null;
        rev=0;
        data=mn=INF;
        pos=-1;
    }

    void init()
    {
        *null=Node();
    }
    void rotate(Node *k)
    {
        Node *r=k->fa;
        int x=k->chlr();
        r->ch[x]=k->ch[x^1];
        k->ch[x^1]->fa=r;
        if (!r->is_rt())
            r->fa->ch[r->chlr()]=k;
        k->fa=r->fa;
        k->ch[x^1]=r;
        r->fa=k;
        r->pushup();
    }
    void fix(Node *k)
    {
        if (!k->is_rt())
            fix(k->fa);
        k->pushdown();
    }
    void splay(Node *k)
    {
        fix(k);
        for (;!k->is_rt();rotate(k))
            if (!k->fa->is_rt())
                rotate(k->fa->chlr()==k->chlr() ? k->fa : k);
        k->pushup();
    }
    Node *access(Node *k)
    {
        Node *r=null;
        for (;k!=null;r=k,k=k->fa)
        {
            splay(k);
            k->ch[1]=r;
        }
        return r;
    }
    void mk_rt(Node *k)
    {
        access(k)->modify_rev();
        splay(k);
    }
    Node *find_rt(Node *k)
    {
        splay(k);
        Node *ret=k;
        while (ret->ch[0]!=null)
        {
            ret=ret->ch[0];
            ret->pushdown();
        }
        return ret;
    }
    void link(Node *k,Node *r)
    {
        mk_rt(r);
        r->fa=k;
    }
    void cut(Node *k,Node *r)
    {
        mk_rt(r);
        access(k);
        splay(k);
        k->ch[0]=null;
        r->fa=null;
        k->pushup();
    }
    int query(Node *k,Node *r)
    {
        mk_rt(r);
        access(k);
        if (find_rt(k)!=find_rt(r))
            return -1;
        splay(k);
        return k->mn==INF ? -1 : k->pos;
    }
}
struct BIT{
    static const int size=200000;
    int d[MAXN];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void modify(int x,int val)
    {
        for (;x<=size;x+=lowbit(x))
            d[x]+=val;
    }
    int query(int l,int r)
    {
        int ret=0;
        for (;r;r-=lowbit(r))
            ret+=d[r];
        for (l--;l;l-=lowbit(l))
            ret-=d[l];
        return ret;
    }
}BI;
LCT::Node G[MAXN<<1];
int main()
{
    LCT::init();
    for (int i=1;i<=400000;i++)
        G[i].id=i;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for (int i=1;i<=m;i++)
            scanf("%d%d",&x[i],&y[i]);
        for (int i=1;i<=n;i++)
        {
            G[i].data=INF;
            G[i].pushup();
        }
        for (int i=1;i<=m;i++)
        {
            G[n+i].data=i;
            G[i].pushup();
        }
        for (int i=1;i<=q;i++)
        {
            scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].id=i;
        }
        sort(Q+1,Q+q+1);
        int now=1;
        for (int i=1;i<=m;i++)
        {
            if (x[i]!=y[i])
            {
                int tmp=LCT::query(&G[x[i]],&G[y[i]]);
                if (tmp!=-1)
                {
                    LCT::cut(&G[x[tmp-n]],&G[tmp]);
                    LCT::cut(&G[y[tmp-n]],&G[tmp]);
                    BI.modify(tmp-n,-1);
                    is_in[tmp-n]=0;
                }

                LCT::link(&G[x[i]],&G[n+i]);
                LCT::link(&G[y[i]],&G[n+i]);
                BI.modify(i,1);
                is_in[i]=1;
            }
            while (now<=q&&Q[now].r==i)
            {
                ans[Q[now].id]=n-BI.query(Q[now].l,Q[now].r);
                now++;
            }
        }
        for (int i=1;i<=n+m;i++)
            G[i].Clear();
        for (int i=1;i<=m;i++)
            if (is_in[i])
            {
                BI.modify(i,-1);
                is_in[i]=0;
            }
        for (int i=1;i<=q;i++)
            printf("%d\n",ans[i]);          
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值