zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",ans,f[ans]);
                ll tt=1;
                tt=segTree[1].sum*f[ans]%mod;
                tt%=mod;
                printf("%lld
    ",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("
    ");
    //        }
            ll tt=1;
            tt=segTree[1].sum*f[ans]%mod;
            tt%=mod;
            printf("%lld
    ",tt);
            //printf("%d %lld
    ",ans,f[ans]);
        }
    } 
  • 相关阅读:
    有关Java2的一些菜鸟疑问
    项目杂记——在后台获取Repeater控件里面的控件
    项目杂记——ASP.net js传参之绑定字段做参数
    项目杂记——超链接里传参
    算法基础 (插入排序、合并排序算法)
    软考操作系统习题分析与总结(一)
    Java中thread类与Runnable接口的区别
    DropDownList绑定中午(列名无效)
    struts 和servlet的关系
    Struts2客户端请求过程
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14359213.html
Copyright © 2011-2022 走看看