Nowcoder9981D.点一成零(并查集+线段树)

链接:https://ac.nowcoder.com/acm/contest/9981/D
来源:牛客网

牛牛拿到了一个n*n的方阵,每个格子上面有一个数字:0或1
行和列的编号都是从0到n-1
现在牛牛每次操作可以点击一个写着1的格子,将这个格子所在的1连通块全部变成0。

牛牛想知道,自己有多少种不同的方案,可以把全部格子的1都变成0?
所谓连通块,是指方阵中的两个正方形共用一条边,即(x,y)和以下4个坐标的数是连通的:(x-1,y)、(x+1,y)、(x,y-1)、(x,y+1)

这个问题对于牛牛来说可能太简单了。于是他将这个问题变得更加复杂:
他会选择一个格子,将这个格子上的数字修改成1(如果本来就是1,那么不进行任何改变),再去考虑“点一成零”的方案数。
牛牛想知道,每次“将某个格子修改成1”之后,“把全部格子的1都变成0”的方案数量。
ps:请注意,每次“将某个格子修改成1”之后,状态会保留到接下来的询问。具体请参考样例描述。
由于方案数可能过大,请对109+710^{9}+7109+7取模

//每次加边
//查询周围四个格子是不是属于一个联通快
//如果是,就合并,同时更新当前联通快数量 
//如果不是,就不变 
#include<bits/stdc++.h>
using namespace std;
const int maxn=550;
const int mod=1e9+7;
//方案是!x,连通快数量
typedef long long ll;
int a[maxn][maxn];
int X[4]={1,0,-1,0};
int Y[4]={0,1,0,-1};
ll f[maxn*maxn];//表示阶乘
string s[maxn];
int father[maxn*maxn];
int num[maxn*maxn];//每个父亲联通快1的数量 
int n;

struct node {
    int l,r;
    ll sum;
}segTree[maxn*maxn*4];
void build (int i,int l,int r) {
    segTree[i].l=l;
    segTree[i].r=r;
    if (l==r) {
        segTree[i].sum=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    segTree[i].sum=(segTree[i<<1].sum==0?1:segTree[i<<1].sum)*(segTree[i<<1|1].sum==0?1:segTree[i<<1|1].sum)%mod;
    segTree[i].sum%=mod;
}
void up (int i,int p,int v) {
    if (segTree[i].l==p&&segTree[i].r==p) {
        segTree[i].sum=v;
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if (p<=mid) up(i<<1,p,v);
    if (p>mid) up(i<<1|1,p,v);
    segTree[i].sum=(segTree[i<<1].sum==0?1:segTree[i<<1].sum)*(segTree[i<<1|1].sum==0?1:segTree[i<<1|1].sum)%mod;
    segTree[i].sum%=mod;
}
void add (int i,int p,int v) {
    if (segTree[i].l==p&&segTree[i].r==p) {
        segTree[i].sum+=v;
        return;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if (p<=mid) add(i<<1,p,v);
    if (p>mid) add(i<<1|1,p,v);
    segTree[i].sum=(segTree[i<<1].sum==0?1:segTree[i<<1].sum)*(segTree[i<<1|1].sum==0?1:segTree[i<<1|1].sum)%mod;
    segTree[i].sum%=mod;
}
ll query (int i,int l,int r) {
    if (segTree[i].l>=l&&segTree[i].r<=r) {
        return segTree[i].sum;
    }
    int mid=(segTree[i].l+segTree[i].r)>>1;
    ll ans=1;
    if (l<=mid) ans*=max(1ll,query(i<<1,l,r));
    if (r>mid) ans*=max(1ll,query(i<<1|1,l,r));
    return ans;
}
int findfather (int x) {
    int a=x;
    while (x!=father[x]) x=father[x];
    while (a!=father[a]) {
        int z=a;
        a=father[a];
        father[z]=x;
    }
    return x;
} 
void Union (int x,int y) {
    x=findfather(x);
    y=findfather(y);
    if (x==y) return;
    father[x]=y;
    num[y]+=num[x];
    num[x]=0;
    up(1,x,num[x]);
    up(1,y,num[y]);
}
int judge (int x,int y) {
    if (x<1||x>n||y<1||y>n) return 0;
    return 1;
}
int isRoot[maxn*maxn];

int main () {
    scanf("%d",&n);
    f[0]=1;
    for (int i=1;i<=n*n;i++) father[i]=i;
    for (int i=1;i<=n*n;i++) f[i]=(f[i-1]*i)%mod;
    for (int i=0;i<n;i++) cin>>s[i]; 
    for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=s[i-1][j-1]-'0',num[(i-1)*n+j]=a[i][j];
    build(1,1,n*n);
    
    for (int i=1;i<=n;i++) {
        for (int j=1;j<=n;j++) {
            for (int k=0;k<4;k++) {
                int x=i+X[k];
                int y=j+Y[k];
                if (!judge(x,y)) continue;
                if (a[i][j]&&a[x][y]) {
                    Union((i-1)*n+j,(x-1)*n+y);
                }
            }
        }
    }
    for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (a[i][j]==1) isRoot[findfather((i-1)*n+j)]++;
    int ans=0;
    for (int i=1;i<=n*n;i++) if (isRoot[i]) ans++;
    //printf("%d\n",ans);
    int q;
    scanf("%d",&q);
    //方案
    //动态维护当前连通块的数量
    //同时用线段树统计所有1的连通块乘积
    //在线段树里更新父亲的值即可 
    while (q--) {
        int x,y;
        scanf("%d%d",&x,&y);
        x++;
        y++;
        if (a[x][y]==1) {
            //printf("%lld\n",ans,f[ans]);
            ll tt=1;
            tt=segTree[1].sum*f[ans]%mod;
            tt%=mod;
            printf("%lld\n",tt);
            continue;
        }
        set<int> st;
        for (int k=0;k<4;k++) {
            int xx=x+X[k];
            int yy=y+Y[k];
            if (!judge(xx,yy)) continue;
            if (a[xx][yy]==0) continue;
            st.insert(findfather((xx-1)*n+yy));
        }
        ans-=(st.size()-1);
        a[x][y]=1;
        num[(x-1)*n+y]++;
        for (int k=0;k<4;k++) {
            int xx=x+X[k];
            int yy=y+Y[k];
            if (!judge(xx,yy)) continue;
            if (a[xx][yy]==0) continue;
            Union((x-1)*n+y,(xx-1)*n+yy);
        }
//        for (int i=1;i<=n;i++) {
//            for (int j=1;j<=n;j++) {
//                printf("%d ",a[i][j]);
//            }
//            printf("\n");
//        }
        ll tt=1;
        tt=segTree[1].sum*f[ans]%mod;
        tt%=mod;
        printf("%lld\n",tt);
        //printf("%d %lld\n",ans,f[ans]);
    }
} 

 

posted @ 2021-02-01 21:43  zlc0405  阅读(85)  评论(0编辑  收藏  举报