zoukankan      html  css  js  c++  java
  • 【BZOJ-3514】Codechef MARCH14 GERALD07加强版 LinkCutTree + 主席树

    3514: Codechef MARCH14 GERALD07加强版

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

    By zhonghaoxi

    Solution

    这应该算是动态图问题吧?? 问了一下ShallWe,用LCT维护动态图问题的一种离线做法是维护一颗 时间最大生成树 ,所以这个也是一样。

    思路非常的巧妙,首先维护一颗 时间最大生成树 ,按时间顺序加边。

    设当前加边为$<u,v>$,如果$u$和$v$属于同一个联通块,则加入$<u,v>$必然会形成环,那么切掉这个环上的边权(时间)最小的边,连上这个边,记这个被切掉的边为$pop_{i}$

    然后这个题要求联通块的个数,然后发现,对于$pop$存在一个性质:

    对于一条边$x$,在边$x$和使边$x$被切的边$y$之间连上的边,是不会与使边$x$被切得边$y$出现环的,即如果$r>y>l>x$则$y$必然会使联通块-1

    所以问题就可以转化为求$[l,r]$中$pop<l$的$pop$的个数,这个可以用主席树去维护,所以答案就是$N-sum$。

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define INF 0x3fffffff
    #define MAXN 400010
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    int N,M,K,T,last;
    struct EdgeNode{int u,v;}edge[MAXN];
    namespace LCT
    {
        int fa[MAXN],son[MAXN][2],id[MAXN],tim[MAXN]; bool rev[MAXN];
        inline void Init() {for (int i=1; i<=N; i++) id[i]=i,tim[i]=INF;}
        inline bool is_root(int x) {return !fa[x] || son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
        inline int Min(int x,int y) {return tim[x]<tim[y]? x:y;}
        inline void Update(int x)
        {
            id[x]=x;
            if (son[x][0]) id[x]=Min(id[x],id[son[x][0]]);
            if (son[x][1]) id[x]=Min(id[x],id[son[x][1]]);
        }
        inline void Rev(int x) {if (!x) return; swap(son[x][0],son[x][1]),rev[x]^=1;}
        inline void Pushdown(int x)
        {
            if (!x) return;
            if (rev[x]) Rev(son[x][0]),Rev(son[x][1]),rev[x]^=1;
        }
        inline void Rotate(int x)
        {
            int y=fa[x],w=son[y][1]==x,z=fa[y];
            son[y][w]=son[x][w^1];
            if (son[x][w^1]) fa[son[x][w^1]]=y;
            if (son[z][0]==y) son[z][0]=x; else if (son[z][1]==y) son[z][1]=x;
            fa[x]=z; fa[y]=x; son[x][w^1]=y; Update(y);
        }
        int stack[MAXN];
        inline void Splay(int x)
        {
            int t=x,top=0,y; stack[++top]=x;
            while (!is_root(t)) stack[++top]=t=fa[t];
            while (top) Pushdown(stack[top--]);
            while (!is_root(x))
                {
                    y=fa[x];
                    if (!is_root(y))
                        if ((son[fa[y]][0]==y)^(son[y][0]==x)) Rotate(x);
                            else Rotate(y);
                    Rotate(x);
                }
            Update(x);
        }
        inline void Access(int x) {for (int y=0; x; y=x,x=fa[x]) Splay(x),son[x][1]=y,Update(x);}
        inline void Makeroot(int x) {Access(x); Splay(x); Rev(x);}
        inline void Link(int x,int y) {Makeroot(x); fa[x]=y; Access(x);}
        inline void Cut(int x) {Access(x); Splay(x); fa[son[x][0]]=0; son[x][0]=0; Update(x);}
        inline void Cut(int x,int y) {Makeroot(x); Access(y); Cut(y);}
        inline int Find(int x) {Access(x); Splay(x); while (son[x][0]) x=son[x][0]; return x;}
        inline int Query(int x,int y) {Makeroot(x); Access(y); Splay(y); return id[y];}
    }using namespace LCT;
    
    namespace PrTree
    {
        int root[MAXN],lson[MAXN*20],rson[MAXN*20],sum[MAXN*20],sz;
        inline void Insert(int l,int r,int &now,int par,int val)
        {
            now=++sz; sum[now]=sum[par]+1;
            if (l==r) {return;}
            lson[now]=lson[par],rson[now]=rson[par];
            int mid=(l+r)>>1;
            if (val<=mid) Insert(l,mid,lson[now],lson[par],val);
                else Insert(mid+1,r,rson[now],rson[par],val);
        }
        inline int Query(int l,int r,int L,int R,int val)
        {
            if (r<=val) return sum[R]-sum[L];
            int mid=(l+r)>>1;
            if (val<=mid) return Query(l,mid,lson[L],lson[R],val);
                else return Query(l,mid,lson[L],lson[R],val)+Query(mid+1,r,rson[L],rson[R],val);
        }
    }using namespace PrTree;
    int pop[MAXN];
    int main()
    {
        N=read(),M=read(),K=read(),T=read();
        for (int i=1; i<=M; i++) edge[i].u=read(),edge[i].v=read();
        LCT::Init();
        for (int i=1; i<=M; i++)
            {
                int u=edge[i].u,v=edge[i].v;
                if (u==v) {pop[i]=i; continue;}
                if (LCT::Find(u)==LCT::Find(v))
                    {
                        pop[i]=LCT::Query(u,v);
                        LCT::Cut(u,pop[i]); LCT::Cut(v,pop[i]);
                        pop[i]-=N;
                    }
                id[i+N]=i+N,tim[i+N]=i;
                LCT::Link(u,i+N); LCT::Link(v,i+N);
            }
    //    for (int i=1; i<=M; i++) printf("%d  ",pop[i]); puts("");
        for (int i=1; i<=M; i++) PrTree::Insert(0,M,root[i],root[i-1],pop[i]);
        while (K--)
            {
                int L=read(),R=read();
                if (T) L^=last,R^=last;
                printf("%d
    ",last=N-Query(0,M,root[L-1],root[R],L-1));
            }
        return 0;
    }
    

      

  • 相关阅读:
    nignx简单操作
    nginx的原理
    nginx简单了解
    操作数栈
    静态变量与局部变量
    遇到C语言内存错误怎么办?一定要找准这六个原因
    千万不要以为程序员是靠技术生存!六句话改变你对程序员的认知
    关于C语言Switch语句,先学这些技巧够不够?
    作为一个码农的悲哀:我是架构师,而你不是
    引用不如指针强大?C++引用,为你深度解析
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6213784.html
Copyright © 2011-2022 走看看