10から0を指す

制限時間:C / C ++ 2秒、他の言語4秒
スペース制限:C / C ++ 262144K、他の言語524288K
64ビットIO形式:%lld
タイトルの説明
Niuniuは、各グリッドに番号が付いたn * nの正方行列を取得しました:0または1の
行と列には、0からn-1までの番号が付けられます。
これで、操作するたびに、1のグリッドをクリックして、このグリッド内の1の接続されたすべてのブロックを0に変更できます。
Niuniuは、グリッドのすべての1を0に変更できるスキームがいくつあるか知りたいですか?
いわゆる接続ブロックとは、正方行列の2つの正方形が1つの辺を共有すること、つまり(x、y)であり、次の4つの座標番号が接続されていることを意味します:(x-1、y)、(x + 1、y )、(x、y-1)、(x、y + 1)の問題は
、Niuniuにとって単純すぎる可能性があります。そこで彼はこの問題をより複雑にしました。
彼はグリッドを選択し、このグリッドの数を1に変更し(1の場合は変更なし)、「ゼロへのポイント」プランの数を検討しました。
Niuniuは、「特定のグリッドを1に変更」した後、「すべてのグリッドのすべての1を0に変更する」スキームの数を知りたいと考えています。
ps:「グリッドを1に変更」するたびに、ステータスは次のクエリまで保持されることに注意してください。詳細については、サンプルの説明を参照してください。
プランの数が多すぎる可能性があるため、1e9 +7のモジュラスを使用してください

入力の説明:
最初の行(1 <= n <= 500)にnを入力し、
次にnn行に各行に長さnnの文字列を入力します。文字列には「0」または「1」の文字のみを含めることができます。正方行列全体
次に、クエリの数を表す数値kkを入力します。(1 <= k <= 10 ^ 5)
後続のk行の各行に2つの整数xとyがあります。これは、x行とy列の数を
10≤xに変更する変更操作を表します。 y≤n-1
出力の説明:
番号を変更する操作ごとに、現在の計画番号を出力します

入る:

3
100
001
000
3
0 1
1 1
1 2

出力:

4
4
4

アイデア:コア:
ユニオンチェックセット(チェックセットMiao Zaiに参加>-<)は、
最初に質問を変更する前に行う方法を確認し、行列を取得し、接続された1に遭遇した場合は、それらをセットに入れてからカウントします。
タイトルを変更した後も、元の操作は同じです。変更された1は、最初は独立した接続ブロックと見なされ、周囲に1がある場合はマージされます。1の場合、変更しても意味がない場合は、出力するだけです。最後の結果を直接。
アイデアは非常に単純で、書くべき詳細がたくさんあります。注意してください
。ps:マージ関数の父と息子が逆になり、3時間調整されます=。=


//1->0
//并查集
//改题前,方案数=连通块数!*所有连通块面积乘积和
//改题后,模拟点0成1,再算
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
int n;
const int p=1e9+7;
int fa[1000005]; //集合根
int siz[1000005]; //每个集合的大小
int dir[][2]={
    
    {
    
    1,0},{
    
    -1,0},{
    
    0,1},{
    
    0,-1}}; //四个方向
char mp[505][505];// 存图
void print()
{
    
    
    for(int i=1;i<=(n+1)*(n+1);i++)
        cout<<siz[i]<<" ";
    cout<<endl;
}
ll qsm(int a,int b)
{
    
    
    ll temp=a,ans=1;
    while( b)
    {
    
    
        if( b & 1) ans=(ans*temp)%p;
        temp=(temp*temp)%p;
        b>>=1;
    }
    return ans%p;
}
ll ni(int x) //除法取模,逆元
{
    
    
    return qsm(x,p-2);
}
int find(int x)
{
    
    
    return fa[x] == x? x: fa[x]=find(fa[x]);
}

void merge(int i, int j)
{
    
    
    int x=find(i);
    int y=find(j);
    if(x!=y) //若不是一个集合的,合并,集合容量增加
        siz[x]+=siz[y],fa[y]=x;
}

int main()
{
    
    
    scanf("%d",&n);
    int cnt=0;
    ll ans=1;

    for(int i=0;i<=(n+1)*(n+1);i++) //集合初始化
        fa[i]=i,siz[i]=1;

    for(int i=1;i<=n;i++)   scanf("%s",mp[i]+1);

//    for(int i=1;i<=n;i++)   printf("%s",mp[i]+1);
    for(int i=1;i<=n;i++)  // 连通块放在一个集合里
    for(int j=1;j<=n;j++)
    {
    
    
        if(mp[i][j]=='1')
        {
    
    
        if(mp[i-1][j]=='1')  merge(i*n+j,(i-1)*n+j);// 用编号表示坐标  i*n+j表示第i行第j个
        if(mp[i+1][j]=='1')  merge(i*n+j,(i+1)*n+j);
        if(mp[i][j-1]=='1')  merge(i*n+j,i*n+j-1);
        if(mp[i][j+1]=='1')  merge(i*n+j,i*n+j+1);
        }
    }
    //计算集合数量
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
    {
    
    
        if( fa[i*n+j]==i*n+j && mp[i][j]=='1') // 如果自己能代表一个连通块集合就算进去
        {
    
    
            cnt++;
            ans=(ans*siz[i*n+j])%p; // 所有连通块面积乘积
        }
    }

    for(int i=1;i<=cnt;i++) ans=(ans*i)%p; // 乘上连通块数目阶乘
    //修改后
    int k;
    scanf("%d",&k);
    while(k--)
    {
    
    
        int x,y;
        scanf("%d %d",&x,&y);
        x++,y++;

        if(mp[x][y]=='1') //原来就是1,直接输出原来答案
        {
    
    
            printf("%lld\n",ans);

            continue;
        }
        mp[x][y]='1';
        cnt++; //先将生成的1,当成独立的连通块处理
        ans=ans*cnt%p;//更新答案

        for(int i=0;i<4;i++)
        {
    
    
            // 注意给的xy 表示行列
            int xx=x+dir[i][0];
            int yy=y+dir[i][1];
            if(mp[xx][yy]=='1')
            {
    
    
            int f1=find(xx*n+yy);
            int f2=find(x*n+y);
            //  如果不是连通块,再合并,因为可能一个连通块四面楚歌的情况,会重复算
            if(f1!=f2)
            {
    
    
                ans=(ans*ni(cnt))%p; //先除掉原来连通块的数据
                ans=(ans*ni(siz[f1]))%p;
                ans=(ans*ni(siz[f2]))%p;
                ans=(ans*(siz[f1]+siz[f2]))%p;
                cnt--;
                merge(f1,f2); //再乘上新的连通块数据,注意cnt不用乘了

            }
            }
        }
        printf("%lld\n",ans);
    }


}

おすすめ

転載: blog.csdn.net/m0_53688600/article/details/113654459