点一成零

点一成零

https://ac.nowcoder.com/acm/contest/9981/D

点一成零

首先我们找出再操作之前有多个方案

并查集操作 找出有多少个连通块 然后阶乘再乘上每一个连通块里的点的个数

这里还是好理解的 就假设一个连通块里有8个另一个连通块里有7个 那么我的方案就有可以先点击8个的也可以先点击7个的 这里就是2*1也就是2的阶乘 这个很容易推广理解 然后我在点击8个的时候 可以点击8个里的任意一个 因此 总的方案数就是

2! * 8 * 7

然后我们继续往下操作 这个的难点在于不同情况的处理 可以大致分为三种情况来处理这个修改成1的操作

1,这个本来就是1 ,因此修改它没有意义,直接输出原来的值即可

2,这个点是一个孤立的点 ,因此修改的得到的就是一个新的连通块,所以我们只需要再原来的基础上乘一个k+1(原来有k的连通块)

这里也不难理解 因为就相当于再原来的基础上多了一个只有一个点的连通块

3,这个点是连接了一个或者多个连通块,那么这里就要注意了,我们应该依次处理四个方向,每连接了一个点,我们在2的基础上,就要减少一个连通块,同时除去两个连通块对答案的作用(相当于就是两个连通块合成了一个),然后再乘上一个新的连通块对答案的影响 这里看代码就可以了

然后这里涉及到(a/b)%p这个逆元操作 因为%不能进行分配律,所以要进行逆元操作 具体看代码即可

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
const ll mod = 1e9 + 7;
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)    putchar(op); return; }    char F[40]; ll tmp = x > 0 ? x : -x;    if (x < 0)putchar('-');    int cnt = 0;    while (tmp > 0) { F[cnt++] = tmp % 10 + '0';        tmp /= 10; }    while (cnt > 0)putchar(F[--cnt]);    if (op)    putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }


int n , k;
char mp[510][510];
int fa[510 * 510];
int siz[510 * 510];
int dir[4][2] = {1,0,-1,0,0,-1,0,1};

int find(int x){
    return fa[x] == x ? x :fa[x] = find(fa[x]);
}

void merge(int x , int y){
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        siz[fx] += siz[fy] , fa[fy] = fx;
}

ll ni(int x){
    return qpow(x , mod - 2);
}

int main(void){
    scanf("%d" , &n);
    for(int i = 1 ; i <= n ; ++i){
        scanf(" %s" , mp[i] + 1);
    }
    n++;
    for(int i = 0 ; i <= n * n ; ++i){
        fa[i] = i , siz[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);
                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);
            }
        }
    }
    int cnt = 0;
    ll ans = 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]) % mod;
        }
    }

    for(int i = 1 ; i <= cnt ; ++i){
        ans = (ans * i) % mod;
    }

    scanf("%d" , &k);
    while(k--){
        int x , y ;
        scanf("%d%d" , &x , &y);
        x++ , y++;

        if(mp[x][y] == '1'){
            printf("%lld\n" , ans);
            continue;
        }

        mp[x][y] = '1';
        cnt++;
        ans = ans * cnt % mod;
        for(int i = 0 ; i < 4 ; ++i){
            int dx = x + dir[i][0];
            int dy = y + dir[i][1];
            if(mp[dx][dy] == '1'){
                int f1 = find(x * n + y);
                int f2 = find(dx * n + dy);

                if(f1 != f2){
                    ans = ans * ni(cnt) % mod;
                    ans = ans * ni(siz[f1]) % mod;
                    ans = ans * ni(siz[f2]) % mod;
                    ans = ans * (siz[f1] + siz[f2]) % mod;
                    cnt--;
                    merge(f1 , f2);
                }
            }
        }
        printf("%lld\n" , ans);
    }
    return 0;
}

//代码就是看的雨巨巨的 雨巨巨就是讲题的那位大佬
全部评论

相关推荐

宇智波爱学习:我还没收到笔试
投递荣耀等公司10个岗位
点赞 评论 收藏
分享
牛客5655:其他公司的面试(事)吗
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务