公式
D - 回転暗号の解読 / Decryption of the Rotation Cipher 解説
by
D - 回転暗号の解読 / Decryption of the Rotation Cipher 解説
by
kyopro_friends
\(\mathrm{cost}[i][j][k]\) を \(S_i\) と \(T_j\) のシフト量 \(k\) での不一致数とします。
全ての \((i,j,k)\) についての \(\mathrm{cost}\) を求めることは \(O(N^2M^2)\) でできます。
求める答えは \(\min_P \min_k \sum_{i=1}^{N}\mathrm{cost}[i][P_i][k]\) です。ここで \(\min\) は \(P\) は \((1,2,\dots,N)\) の並び替えである数列 \(P\) 全てについてを取ります。これは、全ての \((i,j,k)\) で \(\mathrm{cost}\) がわかっているとき、数式の通り計算することで \(O(N!NM)\) で求めることができます。
クエリ 1,2 では \(\mathrm{cost}[i][*][*]\) が、クエリ 3 では \(\mathrm{cost}[*][j][*]\) が変化しますが、変化した文字にのみ注目することで各変化は \(O(1)\) で計算でき、全体で \(O(NM)\) で更新できます。
よって以上より、\(O(N^2M^2+QN!NM)\) でこの問題を解くことができます。
実装例 (C++)
#include<bits/stdc++.h>
using namespace std;
int calc[5][5][500];
int main(){
int n, m, q;
cin >> n >> m >> q;
vector<string> s(n), c(n), t(n);
for(int i=0; i<n; i++) cin >> s[i] >> c[i];
for(int i=0; i<n; i++) cin >> t[i];
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
for(int k=0; k<m; k++){
for(int p=0; p<m; p++){
if(c[i][p] == '0' && s[i][p] != t[j][(p+k)%m]){
calc[i][j][k]++;
}
}
}
}
}
for(int _=0; _<q; _++){
int type, i, p;
char x;
cin >> type >> i >> p >> x;
i--, p--;
if(type == 1){
for(int j=0; j<n; j++){
for(int k=0; k<m; k++){
if(c[i][p] == '0' && s[i][p] != t[j][(p+k)%m]){
calc[i][j][k]--;
}
if(c[i][p] == '0' && x != t[j][(p+k)%m]){
calc[i][j][k]++;
}
}
}
s[i][p] = x;
}else if(type == 2){
for(int j=0; j<n; j++){
for(int k=0; k<m; k++){
if(c[i][p] == '0' && s[i][p] != t[j][(p+k)%m]){
calc[i][j][k]--;
}
if(x == '0' && s[i][p] != t[j][(p+k)%m]){
calc[i][j][k]++;
}
}
}
c[i][p] = x;
}else if(type == 3){
int j = i;
for(int i=0; i<n; i++){
for(int k=0; k<m; k++){
if(c[i][(p-k+m)%m] == '0' && s[i][(p-k+m)%m] != t[j][p]){
calc[i][j][k]--;
}
if(c[i][(p-k+m)%m] == '0' && s[i][(p-k+m)%m] != x){
calc[i][j][k]++;
}
}
}
t[j][p] = x;
}
int ans = 1e9;
vector<int>perm(n);
for(int i=0; i<n; i++){
perm[i] = i;
}
do{
for(int k=0; k<m; k++){
int tans = 0;
for(int i=0; i<n; i++){
tans += calc[i][perm[i]][k];
}
ans = min(ans, tans);
}
}while(next_permutation(perm.begin(), perm.end()));
cout << ans << endl;
}
}
実装例 (Python)
import itertools
N, M, Q = map(int, input().split())
S = []
C = []
for _ in range(N):
S.append(list(input()))
C.append(list(input()))
T = [list(input()) for _ in range(N)]
# calc[i][j][k] = S[i] とrot(T,r) の差
calc = [[[0]*M for _ in range(N)] for _ in range(N)]
for i in range(N):
for j in range(N):
for k in range(M):
for p in range(M):
if C[i][p] == '0' and S[i][p] != T[j][(p+k)%M]:
calc[i][j][k] += 1
for _ in range(Q):
t, i, p, x = input().split()
i = int(i) - 1
p = int(p) - 1
if t == '1':
for j in range(N):
for k in range(M):
if C[i][p] == '0' and S[i][p] != T[j][(p+k)%M]:
calc[i][j][k] -= 1
if C[i][p] == '0' and x != T[j][(p+k)%M]:
calc[i][j][k] += 1
S[i][p] = x
elif t == '2':
for j in range(N):
for k in range(M):
if C[i][p] == '0' and S[i][p] != T[j][(p+k)%M]:
calc[i][j][k] -= 1
if x == '0' and S[i][p] != T[j][(p+k)%M]:
calc[i][j][k] += 1
C[i][p] = x
elif t == '3':
j = i
for i in range(N):
for k in range(M):
if C[i][(p-k)%M] == '0' and S[i][(p-k)%M] != T[j][p]:
calc[i][j][k] -= 1
if C[i][(p-k)%M] == '0' and S[i][(p-k)%M] != x:
calc[i][j][k] += 1
T[j][p] = x
ans = 10**18
for perm in itertools.permutations(range(N)):
for k in range(M):
ans = min(ans, sum(calc[i][perm[i]][k] for i in range(N)))
print(ans)
