XUANMING'S JOURNAL屏幕在深夜微微发亮,我心在远方。
← 回到笔记
FIELD NOTES

题解 P4121 【[WC2005]双面棋盘】

这篇笔记的路线图

线段树分治模板题。

Part.1

Q:如果没有修改,我们怎么做?

A:用并查集维护连通性。

直接扫一遍,我们加进来一个数,就先令答案++,看一下四个方向,如果某个方向与当前点不在同一个连通块中,那么就合并并查集,令答案–。

Part.2

有修改?并查集不支持删除?怎么办?乱搞

这里我们可以请出我们解决这类问题的神器,线段树分治。(没有学过的请左转【模板】线段树分治)

我们知道每个位置在时间轴上有时是0,有时是1,那么对应的就是一段一段的区间,由线段树的性质,每个区间都可以拆成log个小区间,我们只用先在那些位置挂上修改操作,再跑一遍dfs,走到对应点时修改,回退时再撤销即可,走到叶节点时再输出答案即可。

总时间复杂度$O(mlog(n^2+m))$

具体实现可以看代码。

#define Id(x,y) ((x-1)*n+y)
using namespace std;
typedef pair<int,int> pii;
const int maxn=205;
const int dx[]={1,0,-1,0};
const int dy[]={0,1,0,-1};

int mp[maxn][maxn],fa[maxn*maxn],dep[maxn*maxn],n,m,ans[2],pre[maxn][maxn],cnt;
struct Query
{
    int l,r,val,x,y;
}q[maxn*maxn*2];

stack<pii> st;
vector<int> wife[maxn*maxn*4];

template<typename T>
inline void read(T &x)
{
    char c;int f=1;
    while(!isdigit(c=getchar())) (c=='-')&&(f=-1);
    x=c^48;
    while(isdigit(c=getchar())) x=x*10+(c^48);
    x*=f;
}

void modify(int u,int l,int r,int x,int y,int id)
{
    if(x<=l&&r<=y) return void(wife[u].push_back(id));
    int mid=(l+r)>>1;
    if(x<=mid) modify(u<<1,l,mid,x,y,id);
    if(y>mid) modify(u<<1|1,mid+1,r,x,y,id);
}

inline int findf(int x)
{
    while(fa[x]!=x) x=fa[x];
    return x;
}

void merge(int u,int v)
{
    if(u==v) return;
    if(dep[u]>dep[v]) swap(u,v);
    st.push(make_pair(u,dep[u]==dep[v]));
    fa[u]=v;dep[v]+=(dep[u]==dep[v]);
}

void solve(int u,int l,int r)
{
    int siz=st.size(),ans0=ans[0],ans1=ans[1];
    for(const auto &i:wife[u])
    {
        ans[q[i].val]++;
        mp[q[i].x][q[i].y]=q[i].val;
        for(int j=0,pref=0;j<4;++j)
        {
            int tx=q[i].x+dx[j],ty=q[i].y+dy[j];
            if(tx<=0||ty<=0||tx>n||ty>n) continue;
            if(mp[q[i].x][q[i].y]==mp[tx][ty])
            {
                int x=findf(Id(q[i].x,q[i].y)),y=findf(Id(tx,ty));
                if(x==y) continue;
                --ans[q[i].val];merge(x,y);
            }
        }
    }
    if(l==r) printf("%d %d\n",ans[1],ans[0]);
    else
    {
        int mid=(l+r)>>1;
        solve(u<<1,l,mid);solve(u<<1|1,mid+1,r);
    }
    for(const auto &i:wife[u])
        mp[q[i].x][q[i].y]=-1;
    ans[0]=ans0;ans[1]=ans1;
    while(st.size()>siz)
    {
        int x=st.top().first;
        dep[fa[x]]-=st.top().second;
        fa[x]=x;st.pop();
    }
}

int main()
{
    int x,y;
    read(n);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            read(mp[i][j]);
    read(m);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            q[Id(i,j)]=(Query){1,m,mp[i][j],i,j},pre[i][j]=Id(i,j);
    for(int i=1;i<=n*n;++i) fa[i]=i;
    cnt=n*n;
    for(int i=1;i<=m;++i)
    {
        read(x);read(y);
        q[pre[x][y]].r=i-1;
        q[++cnt]=(Query){i,m,mp[x][y]^=1,x,y};
        pre[x][y]=cnt;
    }
    for(int i=1;i<=cnt;++i)
        if(q[i].l<=q[i].r) modify(1,1,m,q[i].l,q[i].r,i);
    memset(mp,-1,sizeof(mp));
    solve(1,1,m);
    return 0,qwq;
}

输入关键词,搜索全部笔记。