CF2256E 思路分享(网格图重心,通信,exgcd)

https://codeforces.com/contest/2256/problem/E

题意

这是一道通信题.

first run

给定 \(n\times n\) 黑白网格图,令黑色格子数量为 \(\omega\),保证 \(\gcd(\omega,n)=1\),同时给定目标点 \((x,y)\). 可以进行一次操作:将任意两个格子内容互换.

在 first run 结束后,网格会被任意进行下列操作任意多次.

  • 循环平移行或列.

  • 顺时针旋转 \(90^\circ\).

  • 镜像翻转.

  • 颜色全部取反.

second run

给定操作后的网格图,找出 first run 中的目标点 \((x,y)\),目标点会随着 first run 结果之后的网格变换改变位置(除了颜色取反).

\(2\le n \le 800\).

思路

\(S\) 为黑色格子的集合,在 \(\gcd(\omega,n)=1\) 的情况下,\(\omega\) 在模 \(n\) 意义下逆元存在,可以定义网格图重心:

\[C=\frac{\sum_{(r,c)\in S}{(r,c)}}{\omega} \pmod n \]

可以证明,\(C\) 会随着上述前三条操作变换,同时反色不改变 \(C\) 的位置.

因此,我们希望在 first run 中将目标点 \((x,y)\) 调整为网格图重心,需要黑色格子偏移量 \(\Delta = (x\cdot \omega - \sum{r},y\cdot \omega - \sum{c})\),因此可以寻找满足以下条件的黑色格子:

  • \((r,c)\) 为黑色.

  • \((r,c)+\Delta \pmod n\) 为白色.

交换这两个位置即可,可以证明,在 \(\Delta \ne (0,0)\) 的情况下这样的格子必然存在,是 \(\gcd(\omega,n)=1\) 的性质保证的.

在 second run 中直接计算重心即可,需要用 exgcd 计算 \(\omega\) 的逆元.

时间复杂度 \(\mathcal{O}(n^2)\).

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int inv(int p,int m){
    int x,y;
    function<void(int,int)> exgcd = [&](int a,int b){
        if (b==0){
            x = 1;
            y = 0;
            return;
        }

        exgcd(b,a%b);
        int nx = y;
        int ny = x-a/b*y;
        x = nx;
        y = ny; 
    };
    exgcd(p,m);
    return (x%m+m)%m;
};


void solve1(){
    int n;
    cin >> n;

    int r=0,c=0,w=0;

    auto ck = [&](int x){
        return (x%n+n)%n;
    };

    vector<string> s(n);
    for (int i=0;i<n;i++){
        cin >> s[i];
        for (int j=0;j<n;j++){
            if (s[i][j]=='#'){
                r = ck(r+i);
                c = ck(c+j);
                w++;
            }
        }
    }

    int x,y;
    cin >> x >> y;
    x--,y--;

    int diffx = ck(x*w-r); 
    int diffy = ck(y*w-c);

    if (diffx==0 && diffy==0){
        cout << 1 << ' ' << 1 << ' ' << 1 << ' ' << 1 << endl;
        return;
    }

    for (int i=0;i<n;i++){
        for (int j=0;j<n;j++){
            if (s[i][j]=='#' && s[ck(i+diffx)][ck(j+diffy)]=='.'){
                cout << i+1 << ' ' << j+1 << ' ' << ck(i+diffx)+1 << ' ' << ck(j+diffy)+1 << endl;
                return;
            }
        }
    } 
}

void solve2(){
    int n;
    cin >> n;

    vector<string> s(n);
    int r=0,c=0,w=0;
    for (int i=0;i<n;i++){
        cin >> s[i];
        for (int j=0;j<n;j++){
            if (s[i][j]=='#'){
                r = (r+i)%n;
                c = (c+j)%n;
                w++;
            }
        }
    }

    r = r*inv(w,n)%n;
    c = c*inv(w,n)%n;
    cout << r+1 << ' ' << c+1 << endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    string op;
    cin >> op;

    int t = 1;
    cin >> t;
    while (t--){
        if (op=="first") solve1();
        else solve2();
    }

    return 0;
}
posted @ 2026-08-11 10:55  kzssCCC  阅读(19)  评论(0)    收藏  举报