• [WC2005]双面棋盘(并查集+分治)


    题目描述

    题解

    唉,还是码力不行,写了一个多小时发现想错了又重构了一个多小时。

    这道题意图很显然,动态维护联通块,有一个经典做法就是用LCT维护按照删除时间维护的最大生成树。

    网上还有一种神奇的做法,线段树套并查集,蒟蒻表示不懂。。

    这道题可以利用并查集操作可以撤销这种性质来做。

    线段树分治

    线段树分治可以分两种情况,操作之间独立和操作之间不独立。

    操作之间独立意味着我先完成哪个操作就可以,例如找最优点,有一道例题

    还有一种是操作之间是可以相互影响的,比如说这道题,连通性这种东西和我加的每一条边都有关。

    我们可以按时间分治,先离线找出每条边出现的时间段,把这些时间段加入线段树中,然后在线段树上dfs,进入节点时把所有边加入,删除时栈序撤销来的时候的操作(因为线段树dfs的过程也是压栈弹栈的过程,所以我们可以准确撤销操作),然后在根节点统计答案,联通块的个数为点数-边数,点数这种东西我们可以直接离线维护(我一开始傻了)。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #define N 40002
    #define maxn 209
    using namespace std;
    int n,id[maxn][maxn],f[N],tot,ls[N<<1],rs[N<<1],b[N],bl,w[N],wl,deep[N],num,last[N<<1],m,root,a[maxn][maxn],bian;
    int tag[maxn][maxn][4],anti[4],color[N<<1];
    const int dx[4]={0,1,-1,0};
    const int dy[4]={1,0,0,-1};
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    int find(int x){return f[x]==x?x:find(f[x]);}
    struct node{int id,co;};
    struct rbs{int dep,root,link,co;};
    struct node2{int x,y;};
    vector<node>vec[N<<1];
    vector<rbs>zh[N<<1];
    node2 linko[N<<1];
    void upd(int &cnt,int l,int r,int L,int R,int x,int y){
        if(!cnt)cnt=++tot;
        if(l>=L&&r<=R){vec[cnt].push_back(node{x,y});return;}
        int mid=(l+r)>>1;
        if(mid>=L)upd(ls[cnt],l,mid,L,R,x,y);
        if(mid<R)upd(rs[cnt],mid+1,r,L,R,x,y);
    }
    void solve(int &cnt,int l,int r){
        if(!cnt)cnt=++tot;
        //cout<<l<<"  ****  "<<r<<endl;
        for(int i=0;i<vec[cnt].size();++i){
            int id=vec[cnt][i].id,co=vec[cnt][i].co;
            int x=linko[id].x,y=linko[id].y;
            int xx=find(x),yy=find(y);
            if(xx!=yy){
                if(co)bl++;else wl++;
                if(deep[xx]<deep[yy])swap(xx,yy);
                zh[cnt].push_back(rbs{deep[xx],xx,yy,co});
                f[yy]=xx;deep[xx]=max(deep[xx],deep[yy]+1);
              }
        }    
    //    cout<<b[l]<<" "<<w[l]<<" "<<bl<<" "<<wl<<endl;
        if(l==r){if(l)printf("%d %d
    ",b[l]-bl,w[l]-wl);}
        else{
        int mid=(l+r)>>1;
        solve(ls[cnt],l,mid);
        solve(rs[cnt],mid+1,r);
        }
        while(zh[cnt].size()){
            rbs x=zh[cnt].back();zh[cnt].pop_back();
            deep[x.root]=x.dep;f[x.link]=x.link;
            if(x.co)bl--;else wl--;
        }
    }
    int main(){
        anti[0]=3;anti[3]=0;anti[2]=1;anti[1]=2;
        n=rd();int x,y;
        for(int i=1;i<=n;++i)
          for(int j=1;j<=n;++j){
          a[i][j]=rd(),id[i][j]=++num;
          if(a[i][j])b[0]++;else w[0]++;
        }
        for(int i=1;i<=num;++i)f[i]=i,deep[i]=1;
        memset(last,-1,sizeof(last));
        for(int i=1;i<=n;++i)
          for(int j=1;j<=n;++j)
              for(int k=0;k<2;++k){
                  int xx=i+dx[k],yy=j+dy[k];
                  if(!id[xx][yy])continue;
                  tag[i][j][k]=tag[xx][yy][anti[k]]=++bian;
                  if(a[i][j]==a[xx][yy])last[bian]=0,color[bian]=a[i][j];
                  linko[bian]=node2{id[i][j],id[xx][yy]};
              }
        m=rd();
        for(int i=1;i<=m;++i){
            x=rd();y=rd();b[i]=b[i-1];w[i]=w[i-1];
            if(a[x][y])b[i]--,w[i]++;else b[i]++,w[i]--;
            for(int k=0;k<4;++k){
              int xx=x+dx[k],yy=y+dy[k],_id=tag[x][y][k];
              if(!_id)continue; 
              if(a[x][y]==a[xx][yy])
                upd(root,0,m,last[_id],i-1,_id,a[x][y]),last[_id]=-1;
              else last[_id]=i,color[_id]=a[xx][yy];
            }
            a[x][y]^=1;
        }
        for(int i=1;i<=n;++i)
          for(int j=1;j<=n;++j)
            for(int k=0;k<2;++k){
              int xx=i+dx[k],yy=j+dy[k],_id=tag[i][j][k];
              if(~last[_id])upd(root,0,m,last[_id],m,_id,color[_id]);
            }
        solve(root,0,m);
        return 0; 
    }
  • 相关阅读:
    List<T>Find方法,FindAll方法,Contains方法,Equals方法
    C#SerialPort如何读取串口数据并显示在TextBox上
    49、css属性相关
    40、协程
    45、mysql 储存过程
    37、进程之间的通信
    38、线程及其方法
    39、多线程和线程池
    33、验证客户端的合法性、socketserver模块
    32、黏包的解决方式、struct模块
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10165315.html
Copyright © 2020-2023  润新知