bzoj3514Codechef MARCH14 GERALD07加强版

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 400005
#define maxm 200005
#define maxk 4000005
using namespace std;

int n,m,k,type,ans,Ans,size,fa[maxn],son[maxn][2],sm[maxn],Val[maxm],val[maxn],sum[maxk],root[maxm],lc[maxk],rc[maxk];
bool rev[maxn];
struct date{
    int u,v;
}wi[maxm];

struct note{
    bool which(int x){
        return son[fa[x]][1]==x;
    }
    bool isroot(int x){
        return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
    }
    void update(int x){
        sm[x]=val[x];
        if (son[x][0]) sm[x]=min(sm[x],sm[son[x][0]]);
        if (son[x][1]) sm[x]=min(sm[x],sm[son[x][1]]);
    }
    void pushdown(int x){
        if (!rev[x]) return;
        rev[x]^=1,swap(son[x][0],son[x][1]);
        if (son[x][0]) rev[son[x][0]]^=1;
        if (son[x][1]) rev[son[x][1]]^=1;
    }
    void relax(int x){
        if (!isroot(x)) relax(fa[x]);
        pushdown(x);
    }
    void rotata(int x){
        int y=fa[x],d=which(x),dd=which(y);
        if (!isroot(y)) son[fa[y]][dd]=x; fa[x]=fa[y];
        fa[son[x][d^1]]=y,son[y][d]=son[x][d^1];
        fa[y]=x,son[x][d^1]=y;
        update(y);
    }
    void splay(int x){
        relax(x);
        while (!isroot(x)){
            if (isroot(fa[x])) rotata(x);
            else if (which(x)==which(fa[x])) rotata(fa[x]),rotata(x);
            else rotata(x),rotata(x);
        }
        update(x);
    }
    void access(int x){
        for (int p=0;x;x=fa[x]){
            splay(x);
            son[x][1]=p;
            p=x;
            update(x);
        }
    }
    void make_root(int x){
        access(x);
        splay(x);
        rev[x]^=1;
    }
    void link(int x,int y){
        make_root(x);
        fa[x]=y;
    }
    void cut(int x,int y){
        make_root(x);
        access(y);
        splay(y);
        son[y][0]=fa[x]=0;
        update(y);
    }
    void split(int x,int y){
        make_root(x);
        access(y);
        splay(y);
    }
    int find_root(int x){
        access(x);
        splay(x);
        while (son[x][0]) x=son[x][0];
        return x;
    }
    int find(int x,int y){
        split(x,y);
        return sm[y];
    }
}lct;

void insert(int &k,int p,int l,int r,int x){
    k=++size,sum[k]=sum[p]+1;
    int mid=(l+r)/2;
    if (l==r) return;
    if (x<=mid) rc[k]=rc[p],insert(lc[k],lc[p],l,mid,x);
    else lc[k]=lc[p],insert(rc[k],rc[p],mid+1,r,x);
}

void insert(int id,int x){
    insert(root[id],root[id-1],0,m+1,x);
}

void Query(int k1,int k2,int l,int r,int x,int y){
    if (!k1&&!k2) return;
    if (l>=x&&r<=y){
        Ans+=(sum[k2]-sum[k1]);
        return;
    }
    int mid=(l+r)/2;
    if (x<=mid) Query(lc[k1],lc[k2],l,mid,x,y);
    if (y>mid) Query(rc[k1],rc[k2],mid+1,r,x,y);
}

int query(int x,int y){
    Ans=0;
    int u=root[x-1],v=root[y],l=0,r=m+1,mid;
    Query(u,v,l,r,0,x-1);
    return Ans;
}

int main(){
    int u,v,t1,t2,temp;
    scanf("%d%d%d%d",&n,&m,&k,&type);
    memset(sum,0,sizeof(sum));
    memset(Val,0,sizeof(Val));
    memset(fa,0,sizeof(fa));
    memset(son,0,sizeof(son));
    memset(rev,0,sizeof(rev));
    for (int i=1;i<=m;i++) scanf("%d%d",&wi[i].u,&wi[i].v);
    for (int i=1;i<=n;i++) val[i]=sm[i]=m+1;
    for (int i=1;i<=m;i++) val[n+i]=sm[n+i]=i;
    for (int i=1;i<=m;i++){
        u=wi[i].u,v=wi[i].v;
        if (u==v) Val[i]=m+1;
        if (u==v) continue;
        if (lct.find_root(u)!=lct.find_root(v)){
            Val[i]=0;
            lct.link(n+i,u),lct.link(n+i,v);
        }else{
            temp=lct.find(u,v);
            Val[i]=temp,temp+=n;
            lct.cut(temp,wi[temp-n].u),lct.cut(temp,wi[temp-n].v);
            lct.link(n+i,u),lct.link(n+i,v);
        }
    }
    memset(root,0,sizeof(root));
    for (int i=1;i<=m;i++){
        insert(i,Val[i]);
    }
    ans=0;
    for (int i=1;i<=k;i++){
        scanf("%d%d",&u,&v);
        if (type==1) u^=ans,v^=ans;
        if (u>v) swap(u,v);
        ans=n-query(u,v);
        printf("%d
",ans);
    }
    return 0;
}
View Code

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3514

题目大意:N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

做法:如果要求整个图中联通块的个数,记加入该边后不形成环的边数记为x,则答案为n-x,这题问的是加入编号为[l~r]中的边后联通块的个数,答案便稍微变化了一下,记能使联通块数目-1的边集为{S},答案为n-|S|,问题便成为了:有多少条边属于{S},仔细想想,可以先预处理一个数组val[i],怎么预处理呢?可以按编号依次加入边,有两种情况:*1.如果加入的这条边不形成环,则这条边的权值赋值为0,并加入这条边;*2.加入这条边形成环,记环上编号最小的边的编号为y,这加入的这条边权值为y,并删去编号最小的那条边,加入该边。这个过程用lct模拟即可,比较基础的操作。

得出每条边的权值val[i]后,对于一个询问[L~R],如果一条边的val<l,则该边属于{S},问题便简化为一个数组val,问在区间L~R中权值在0~L-1的个数,显而易见,可持久化线段树轻松搞定。问题得以圆满解决。(Lct+可持久化线段树)

原文地址:https://www.cnblogs.com/OYzx/p/5503187.html