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;
}

浙公网安备 33010602011771号