bzoj3514 Codechef MARCH14 GERALD07加强版

有些纪念意义的题

$N$个点$M$条边的无向图,$Q$次询问保留图中编号在$[l,r]$的边的时候图中的联通块个数,强制在线。

$N,M,Q \leq 200000$

受某远古$CF$题的影响,大力$LCT$硬搞

一个神奇的做法:

令每条边边权为加入时间

搞一个数组$used$,加边的时候如果形成了环,则弹掉边权最小的那条边,并记录$used[i]=j$表示$i$弹掉了$j$

特别地,当i上有一个自环时$used[i]=i$,不弹掉的话$used[i]=0$

这样的话每个询问的答案就是$n-[l,r]$区间上$used$值小于$l$的边的数量

正确性:

如果一条边的$used$值不小于$l$,说明它能跟$[l,r]$上的边连成一个环,对答案没有贡献

如果小于$l$,说明是一条新边,对答案的贡献为$-1$

于是开个树套树/主席树记一下$[l,r]$区间上小于$k$的数的个数就好啦

/**************************************************************
    Problem: 3514
    User: Ez3real
    Language: C++
    Result: Accepted
    Time:30504 ms
    Memory:79436 kb
****************************************************************/
 
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
    int x=0,f=1;char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')f=-f;
    for(;isdigit(ch);ch=getchar())x=10*x+ch-'0';
    return x*f;
}
const int maxn = 400010 , inf = 1e9;
int n,m,q,type,lastans;
namespace LCT
{
    int son[maxn][2],fa[maxn],rev[maxn];
    int mn[maxn],val[maxn];
    int st[maxn],top;
     
    #define lc son[x][0]
    #define rc son[x][1]
    inline int isroot(int x){return (son[fa[x]][0] != x) && (son[fa[x]][1] != x);}
    inline void pushup(int x)
    {
        mn[x] = x;
        if(val[mn[lc]] < val[mn[x]]) mn[x] = mn[lc];
        if(val[mn[rc]] < val[mn[x]]) mn[x] = mn[rc];
    }
    inline void pushdown(int x)
    {
        if(rev[x])
        {
            rev[x] ^= 1; 
            rev[lc] ^= 1;
            rev[rc] ^= 1;
            swap(lc,rc);
        }
    }
    inline void rotate(int x)
    {
        int y=fa[x],z=fa[y];
        int l=(son[y][1] == x),r = l ^ 1;
        if(!isroot(y))son[z][son[z][1] == y] = x;
        fa[x] = z,fa[y] = x, fa[son[x][r]] = y;
        son[y][l] = son[x][r] , son[x][r] = y ;
        pushup(y),pushup(x);
    }
    inline void splay(int x)
    {
        st[top=1]=x;
        for(int i=x;!isroot(i);i=fa[i])st[++top]=fa[i];
        for(int i=top;i;i--)pushdown(st[i]);
        while(!isroot(x))
        {
            int y=fa[x],z=fa[y];
            if(!isroot(y))
            {
                if(son[y][0] == x ^ son[z][0] == y)rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    inline void access(int x)
    {
        for(int y=0;x;y=x,x=fa[x])splay(x),son[x][1] = y,pushup(x);
    }
    inline void makert(int x)
    {
        access(x),splay(x),rev[x]^=1;
    }
    inline void link(int x,int y)
    {
        makert(x),fa[x]=y;
    }
    inline void split(int x,int y)
    {
        makert(x),access(y),splay(y);
    }
    inline void cut(int x,int y)
    {
        split(x,y);
        son[y][0]=fa[x]=0;
    }
    inline int find(int x)
    {
        access(x),splay(x);
        while(son[x][0]) x = son[x][0];
        return x;
    }
    inline int LCquery(int x,int y)
    {
        split(x,y);
        return mn[y];
    }
}
 
namespace HujinTree
{
    int SIZE;
    const int MAXNODE = 4000010;
    int root[MAXNODE];
    int ls[MAXNODE],rs[MAXNODE],nval[MAXNODE];
    inline void insert(int &cur,int l,int r,int pre,int va)
    {
        cur = ++SIZE;
        nval[cur] = nval[pre] + 1;
        if(l == r)return;
        ls[cur] = ls[pre] , rs[cur] = rs[pre];
        int mid = (l+r) >> 1;
        if(va <= mid) insert(ls[cur],l,mid,ls[pre],va);
        else insert(rs[cur],mid+1,r,rs[pre],va);
    }
    inline int query(int cur,int l,int r,int pre,int va)
    {
        if(r == va)return nval[cur] - nval[pre];
        int mid = (l+r) >> 1;
        if(va <= mid)return query(ls[cur],l,mid,ls[pre],va);
        else return nval[ls[cur]] - nval[ls[pre]] + query(rs[cur],mid+1,r,rs[pre],va);
    }
}
 
namespace LCGraph
{
    using namespace HujinTree;
    using namespace LCT;
    struct EDG{int u,v;}es[maxn];
    int used[maxn];
    inline 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;
        for(int i=1;i<=m;i++)es[i].u=read(),es[i].v=read();
        //return 0;
        int temp = n;
        for(int i=1;i<=m;i++)
        {
            int uu = es[i].u, vv = es[i].v;
            if(uu == vv){used[i] = i;continue;}
            if(find(uu) == find(vv))
            {
                int a = LCquery(uu,vv);
                int av = val[a];
                used[i] = av;
                //return 0;
                cut(uu,a),cut(vv,a);
            }
            mn[++temp] = temp;val[temp] = i;
            link(uu,temp) ,link(vv,temp);
        }
        for(int i=1;i<=m;i++)
            insert(root[i],0,m,root[i-1],used[i]);
        //return 0;
        while(q--)
        {
            int l,r;
            l=read(),r=read();
            if(type == 1)l ^= lastans, r ^= lastans;
            lastans = n - query(root[r],0,m,root[l-1],l-1);
            //lastans = query(root[r],0,m,root[l-1],l-1);
            printf("%d\n",lastans);
        }
    }
}
 
int main()
{
    LCGraph::main();
    return 0;
}
View Code

人傻常数大,一开始60s过不了第一个点QAQ

posted @ 2018-02-22 19:25  探险家Mr.H  阅读(222)  评论(0编辑  收藏  举报