• bzoj3514 Codechef MARCH14 GERALD07加强版


    考虑对于一个询问的右端点r

    存在左边的一条边i,会使得存在区间i-1,r的边的时候,连通块数量比存在区间i,r的边少1

    这样要查询的东西就转化成了区间l,r中,i小于l的边的数量,也就是有效边(会使连通块数量减一的边)的数量

    答案就是n-ans

    所以考虑维护最大删除时间生成树

    每加入一条边时,如果成环,则找到边的编号最小的一条边删除掉,这条边就是r边要求的边i

    lct维护生成树,边转化成点,具体实现细节见代码

    注意:pushup的时候要判断一下儿子节点存不存在

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 400100;
    const int INF = 1e9+7;
    
    int n,m,q,type,cnt,la;
    int st[maxn],val[maxn<<1];
    
    int rt[maxn],sum[maxn<<4],lc[maxn<<4],rc[maxn<<4],add[maxn<<4],tot;
    
    struct E{
        int u,v;
    }e[maxn];
    
    struct Node{
        int ch[2];
        int fa;
        int mn,id;
        int rev;
    }t[maxn<<1];
    
    int isroot(int x){ return (t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x); }
    
    void pushup(int x){
        t[x].mn=x;
        if(t[x].ch[0]){ // 要判断儿子节点是否存在 
            if(val[t[t[x].ch[0]].mn]<val[t[x].mn]) t[x].mn=t[t[x].ch[0]].mn; // 更新编号最小的点 
        }
        if(t[x].ch[1]){
            if(val[t[t[x].ch[1]].mn]<val[t[x].mn]) t[x].mn=t[t[x].ch[1]].mn;
        }    
    }
    
    void rever(int x){
        t[x].rev^=1;
        swap(t[x].ch[0],t[x].ch[1]);
    }
    
    void pushdown(int x){
        if(t[x].rev){
            if(t[x].ch[0]) rever(t[x].ch[0]);
            if(t[x].ch[1]) rever(t[x].ch[1]);
            t[x].rev=0;
        }
    }
    
    void rotate(int x){
        int y=t[x].fa,z=t[y].fa;
        int k=(t[y].ch[1]==x);
        if(!isroot(y)) t[z].ch[t[z].ch[1]==y]=x;
        t[x].fa=z;
        t[y].ch[k]=t[x].ch[k^1];
        t[t[x].ch[k^1]].fa=y;
        t[x].ch[k^1]=y;
        t[y].fa=x;
        pushup(y); pushup(x);
    }
    
    int sta[maxn],top;
    void splay(int x){
        top=0; int y=x;
        sta[++top]=y;
        
        while(t[y].fa){
            y=t[y].fa;
            sta[++top]=y;
        }
        while(top) pushdown(sta[top--]);
        
        while(!isroot(x)){
            int y=t[x].fa,z=t[y].fa;
            if(!isroot(y)){
                (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);
            }rotate(x);
        }
        pushup(x);
    }
    
    void access(int x){
        for(int y=0;x;y=x,x=t[x].fa){ splay(x); t[x].ch[1]=y; pushup(x); }
    }
    
    void makeroot(int x){
        access(x);
        splay(x);
        rever(x);
    }
    
    int findroot(int x){
        access(x);
        splay(x);
        while(t[x].ch[0]){
            pushdown(x); // findroot不要忘记pushdown 
            x=t[x].ch[0];
        }
        return x;
    }
    
    void split(int x,int y){
        makeroot(x);
        access(y);
        splay(y);
    }
    
    void link(int x,int y){ makeroot(x); t[x].fa=y; }
    
    void cut(int x,int y){
        makeroot(x);
        access(y);
        splay(y);
        t[y].ch[0]=t[x].fa=0;
    }
    
    int query(int x,int y){
        split(x,y); // 直接把链拎出来询问 
        return t[y].mn;
    }
    
    void build(int &i,int l,int r){
        i=++tot;
        if(l==r) return;
        int mid=(l+r)/2;
        build(lc[i],l,mid);
        build(rc[i],mid+1,r);
    }
    
    void mdf(int &i,int l,int r,int p){ // 主席树标记永久化 
        sum[++tot]=sum[i],add[tot]=add[i],lc[tot]=lc[i],rc[tot]=rc[i];
        i=tot;
        sum[i]++;
        if(l==r){
            add[i]++;
            return; 
        }
        int mid=(l+r)/2;
        if(p<=mid) mdf(lc[i],l,mid,p);
        else mdf(rc[i],mid+1,r,p);
    }
    
    int qry(int i,int ad,int l,int r,int x,int y){
        if(l==x&&r==y){ return sum[i]+ad*(r-l+1); }
        int mid=(l+r)/2;
        if(y<=mid) return qry(lc[i],ad+add[i],l,mid,x,y);
        else if(x>mid) return qry(rc[i],ad+add[i],mid+1,r,x,y);
        else{
            return qry(lc[i],ad+add[i],l,mid,x,mid)+qry(rc[i],ad+add[i],mid+1,r,mid+1,y);
        }
    }
    
    void pre(){
        cnt=n;
        int u,v;
        for(int i=1;i<=m;i++){
            u=e[i].u,v=e[i].v;
            if(u==v){
                st[i]=i;
                continue;
            }
            if(findroot(u)==findroot(v)){
                int tmp=query(u,v);
                int x=val[tmp];
                st[i]=x;
                cut(e[x].u,tmp); cut(e[x].v,tmp);
            }
            ++cnt;
            t[cnt].mn=cnt; val[cnt]=i; // val记录的是边转化成lct中的点的编号对应的原边的编号 
            link(u,cnt); link(v,cnt);
        }
        
        for(int i=1;i<=m;i++){
            rt[i]=rt[i-1];
            mdf(rt[i],0,m,st[i]);
        }
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;}
     
    int main(){
        n=read(),m=read(),q=read(),type=read();
        val[0]=INF;
        for(int i=1;i<=n;i++) t[i].mn=i,val[i]=INF; // 不是边转化成的点,val值要赋为正无穷,防止被更新 
        for(int i=1;i<=m;i++) e[i].u=read(),e[i].v=read();
        
        build(rt[0],0,m);
        pre();
        la=0;
    
        int u,v;
        for(int i=1;i<=q;i++){
            u=read(),v=read();
            if(type==1) u^=la,v^=la;
            la=n-(qry(rt[v],0,0,m,0,u-1)-qry(rt[u-1],0,0,m,0,u-1));
            printf("%d\n",la);
        }
        return 0;
    }
  • 相关阅读:
    vue的选项
    css(3)基础知识查漏补缺
    总结获取网页相关的一些宽高
    vue的全局api(二)
    vue的全局api
    vue的内部指令
    Java File
    Java File IO学习笔记
    systemctl介绍
    java学习笔记一(20180319)
  • 原文地址:https://www.cnblogs.com/tuchen/p/10560558.html
Copyright © 2020-2023  润新知