(离线维护图联通性)BZOJ1018[SHOI2008]堵塞的交通traffic

[SHOI2008]堵塞的交通traffic

  题意:

  题解:

    听说这题正解是线段树,然而我这个蒟蒻并看不懂线段树的做法,然后看见这题的讨论区里提到了一个叫分治并查集的东西,于是我就学了一发这个奇妙的东西并A了这道题.

    分治并查集可以用来离线维护一张图的连通性.首先,我们需要离线,把所有询问读进来,对于每条边,它出现的时间肯定是几段区间,于是可以按照时间建一棵线段树,线段树的每个节点存的是在该节点的时间区间内一直存在的边.建完线段树后我们对这棵线段树进行dfs,每次进入该节点就将该节点的所有边都加上,然后离开该节点就将该节点的所有边删除.这样,我们的删除操作就变成了撤销操作,即我们需要一棵支持合并和撤销的并查集.对于这种并查集,我们不能路径压缩,而是需要启发式合并(将size小的节点并到size大的上).这样我们发现每次合并只会更改2个节点的信息.于是我们就可以把更改前这2个节点的信息存下来,然后撤销的时候赋回原来的值就行了.

    这样这道题就是维护形状比较特殊的图的连通性了.

    代码中对于线段树的每个节点存了一个前向星表示该节点包含的边,并且对于并查集用了一个小更改:对于集合的根节点,它的fa是它的size的相反数,否则它的fa就是它的父节点.

#include<cstdio>
#include<map>
using namespace std;
const int N=100000,lgN=19;
struct query{int op,l,r;}a[N+10];
int ask_id[N+10],ask_cnt,n,m;
int hsh(int x,int y){return (x-1)*n+y;}
int query_id[N*lgN+10],nxt[N*lgN+10],qcnt;
struct node{int l,r,h;}b[N*4+10];
void build_tree(int p,int l,int r){
    b[p].l=l; b[p].r=r;
    if(l!=r){
        int mid=(l+r)/2;
        build_tree(p*2,l,mid); build_tree(p*2+1,mid+1,r);
    }
}
void add(int p,int l,int r,int v){
    if(b[p].l==l&&b[p].r==r){
        query_id[++qcnt]=v; nxt[qcnt]=b[p].h; b[p].h=qcnt; return;
    }
    int mid=(b[p].l+b[p].r)/2;
    if(r<=mid) add(p*2,l,r,v); else if(l>mid) add(p*2+1,l,r,v);
    else{
        add(p*2,l,mid,v); add(p*2+1,mid+1,r,v);
    }
}
map<pair<int,int>,int> M,MM; int fa[N*2+10],record[N*lgN+10][4],rec_cnt;
int getf(int x){for(;fa[x]>0;x=fa[x]); return x;}
void dfs_ans(int p){
    int nowl=rec_cnt+1;
    for(int i=b[p].h;i;i=nxt[i]){
        int l=getf(a[query_id[i]].l),r=getf(a[query_id[i]].r);
        if(l==r) continue; if(fa[l]>fa[r]) swap(l,r);
        record[++rec_cnt][0]=l; record[rec_cnt][1]=fa[l];
        record[rec_cnt][2]=r; record[rec_cnt][3]=fa[r];
        fa[l]+=fa[r]; fa[r]=l;
    }
    if(b[p].l==b[p].r)
        printf("%s\n",getf(a[ask_id[b[p].l]].l)==getf(a[ask_id[b[p].l]].r)?"Y":"N");
    else{
        dfs_ans(p*2); dfs_ans(p*2+1);
    }
    for(;rec_cnt>=nowl;--rec_cnt){
        fa[record[rec_cnt][0]]=record[rec_cnt][1]; fa[record[rec_cnt][2]]=record[rec_cnt][3];
    }
}
int main(){
    scanf("%d",&n); for(int i=1;i<=n*2;++i) fa[i]=-1;
    for(;;){
        char s[2]; int r1,c1,r2,c2;
        scanf("%s",s); if(s[0]=='E') break; ++m;
        scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
        if(s[0]=='O') a[m].op=0;
        else if(s[0]=='C') a[m].op=1;
        else if(s[0]=='A'){
            a[m].op=2; ask_id[++ask_cnt]=m;
        }
        a[m].l=hsh(r1,c1); a[m].r=hsh(r2,c2);
        if(a[m].l>a[m].r) swap(a[m].l,a[m].r);
    }
    if(ask_cnt==0) return 0;
    build_tree(1,1,ask_cnt);
    for(int i=1,j=1;i<=m;++i){
        pair<int,int> P=make_pair(a[i].l,a[i].r);
        if(a[i].op==0){
            if(!M[P]){
                M[P]=j; MM[P]=i;
            }
        }else if(a[i].op==1){
            if(M[P]){
                if(M[P]<=j-1) add(1,M[P],j-1,i); M[P]=0;
            }
        }else ++j;
    }
    for(map<pair<int,int>,int>::iterator it=M.begin();it!=M.end();++it) if(it->second&&it->second<=ask_cnt) add(1,it->second,ask_cnt,MM[it->first]);
    dfs_ans(1); return 0;
}
原文地址:https://www.cnblogs.com/jxcakak/p/7588400.html