CodeChef - GERALD07 Chef and Graph Queries

190 篇文章 2 订阅
11 篇文章 0 订阅

题面

题意

给出一张无向图,每次询问编号L到R的边,问这些边连上之后一共有几个联通块。

做法

听说可以用分块加可持久并查集来做,可惜我不会。
我用的是LCT加树状数组,离线来做。
求连通块的个数可以考虑转化为求此时图中树边的个数,我们可以将询问按照右端点排序,然后随着询问的右端点的右移,来不断向LCT中加边,此时LCT中的边都是树边,将树边存入树状数组中,每次询问L,R这段区间内树边的数量即可。
若向LCT中加边之后会构成环,则将环上编号最小的边去掉(贪心的去想,要使每次询问的树边数量尽可能大,故应保留编号尽可能大的树边)即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define C ch=getchar()
#define INF 0x3f3f3f3f
#define N 400100
#define M 200100
using namespace std;

int T,n,m,Q,son[N][2],fa[N],mn[N],a[M],b[M],sz[M],an[M],faz[M],sum;
bool fz[N];
char ch;
vector<P>pos[N];

inline int read(){static int res;for(C;ch<'0';C);for(res=ch-'0',C;ch>='0';res=res*10+ch-'0',C);return res;}
inline void write(int u){if(u>9) write(u/10);putchar(u%10+'0');}
inline void Min(int &u,int v){if(v<u) u=v;}
inline int lb(int u){return u&(-u);}
inline void add(int u,int v){for(; u<=m; u+=lb(u)) sz[u]+=v;}
inline int ask(int u){int res=0;for(; u; u-=lb(u)) res+=sz[u];return res;}
inline int ff(int u){return u==faz[u]?u:faz[u]=ff(faz[u]);}
inline bool as(int u){return u==son[fa[u]][1];}
inline bool ar(int u){return u!=son[fa[u]][1] && u!=son[fa[u]][0];}

inline void up(int u)
{
    if(!u) return;
    mn[u]=u;
    if(son[u][0]) Min(mn[u],mn[son[u][0]]);
    if(son[u][1]) Min(mn[u],mn[son[u][1]]);
}

inline void down(int u)
{
    if(fz[u])
    {
        swap(son[u][0],son[u][1]);
        fz[son[u][0]]^=1;
        fz[son[u][1]]^=1;
        fz[u]=0;
    }
}

inline void rot(int u)
{
    down(fa[u]),down(u);
    int p=fa[u],d=as(u);
    if(!ar(p)) son[fa[p]][as(p)]=u;
    fa[u]=fa[p];
    fa[p]=u;
    fa[son[u][!d]]=p;
    son[p][d]=son[u][!d];
    son[u][!d]=p;
    up(p),up(u);
}

inline void splay(int u)
{
    int p;
    for(; !ar(u);)
    {
        p=fa[u];
        if(!ar(p))
            as(u)==as(p)?rot(p):rot(u);
        rot(u);
    }
}

inline void acc(int u){int p,q;for(p=u,q=0; p; q=p,p=fa[p]) splay(p),down(p),son[p][1]=q,up(p);}
inline void mr(int u){acc(u),splay(u),fz[u]^=1;}
inline int fr(int u){acc(u),splay(u);for(down(u); son[u][0]; u=son[u][0],down(u));return u;}
inline void spl(int u,int v){mr(u),acc(v),splay(v);}
inline void link(int u,int v){mr(u),fa[u]=v;}
inline void cut(int u,int v){spl(u,v),fa[u]=son[v][0]=0,up(v);}

inline void ade(int w)
{
    int u=a[w],v=b[w];
    if(u==v) return;
    add(w,1);
    if(ff(u)!=ff(v))
    {
        faz[ff(u)]=ff(v);
        sum++;
        link(u+m,w),link(w,v+m);
        return;
    }
    int tmp;
    spl(u+m,v+m);
    tmp=mn[v+m];
    cut(a[tmp]+m,tmp),cut(b[tmp]+m,tmp);
    add(tmp,-1);
    link(u+m,w),link(w,v+m);
}

int main()
{
    int i,j,p,q;
    T=read();
    while(T--)
    {
        sum=0;
        memset(sz,0,sizeof(sz));
        memset(fa,0,sizeof(fa));
        memset(son,0,sizeof(son));
        memset(fz,0,sizeof(fz));
        n=read(),m=read(),Q=read();
        for(i=1;i<=n;i++) mn[i]=faz[i]=i;
        for(i=1;i<=m;i++) mn[i+n]=i;
        for(i=1; i<=m; i++)
            a[i]=read(),b[i]=read();
        for(i=1; i<=Q; i++)
            p=read(),q=read(),pos[q].push_back(mp(p,i));
        for(i=1;i<=m;i++)
        {
            ade(i);
            for(j=0;j<pos[i].size();j++)
            {
                an[pos[i][j].se]=sum-ask(pos[i][j].fi-1);
            }
            pos[i].clear();
        }
        for(i=1;i<=Q;i++) write(n-an[i]),puts("");
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值