间谍过家家
比赛链接
邀请码:ccsu2022yyds
题目描述
众所周知,致远大厦活跃着许多精英,他们在此执行秘密任务,维护这个世界的秩序。
然而,今日注定是不平凡的一天,弗兰克·富兰克林收到情报,有来自邪恶势力的人混入了永康训练基地,企图不利于致远大厦,弗兰克·富兰克林一刻也不敢耽搁,以最快的速度通知了黄昏。黄昏作为西国第一间谍,他不紧不慢地接回在伊甸学院上课的阿尼亚,并打电话告知约尔,准备集全家之力解决这个麻烦。
三人乘坐直升机来到了永康训练基地的上空,他们发现所有训练人员聚集在中间矩形区域形成一个方阵。人员由左上角进入后,站至首行首列(每一行在有人加入后,同行其他人将被挤到下一列,处于尾列的人会被挤到下一行首列,而处于尾行尾列的人将离开)。黄昏因为要控制飞机,此次攻击任务只能拜托约尔,约尔看了看,表示她一次只能清理一整行或者一整列的敌人。
-
作战计划
约尔空降下去,她可在矩形外随意跑动,对任意行列实施攻击。
阿尼亚使用读心术判断谁是敌军谁是友军,不过她对超能力的掌握还不是很好,因此可能出现判断错误需要修正。
黄昏负责计算出仅考虑当前状态所判断的友军、敌军,仅在当次状态变更下进行攻击,完全避免误伤友军,尽可能多的清理敌军,约尔需要实施的最少攻击次数,同时负责将信息传递给组织。
输入描述
-
第一行三个用空格隔开的整数 n n n、 m m m、 q q q。 n n n、 m m m分别表示聚集人员方阵的行列数, q q q表示状态变更以及传递信息的次数。 ( 1 ⩽ n 、 m 、 q ⩽ 5000 ) (1 \leqslant n 、m、q \leqslant 5000) (1⩽n、m、q⩽5000)
-
第二行到第 n + 1 n + 1 n+1行一个 n n n行 m m m列的矩阵,表示敌友初始方阵, 0 0 0代表友军, 1 1 1代表敌军。
-
接下来 q q q行,每行第一个整数 o p i op_i opi。 ( 1 ⩽ i ⩽ q ) (1 \leqslant i \leqslant q) (1⩽i⩽q)
- o p i = = 1 op_i == 1 opi==1 :其后紧随三个空格隔开的整数 x x x、 y y y、 c c c,表示阿尼亚将第 x x x行第 y y y列的人员修正为 c c c。 ( 1 ⩽ x ⩽ n , 1 ⩽ y ⩽ m , 0 ⩽ c ⩽ 1 ) (1 \leqslant x \leqslant n,1 \leqslant y\leqslant m,0 \leqslant c\leqslant 1) (1⩽x⩽n,1⩽y⩽m,0⩽c⩽1)
- o p i = = 2 op_i == 2 opi==2 :其后紧随一个用空格隔开的整数 c c c,表示有一个 c c c人员由左上角进入训练基地。 ( 0 ⩽ c ⩽ 1 ) (0 \leqslant c\leqslant 1) (0⩽c⩽1)
- o p i = = 3 op_i == 3 opi==3:表示将敌友方阵传递给组织。
( 1 ⩽ o p i ⩽ 3 , n ⋅ m ⋅ ∑ i = 1 q [ o p i = = 3 ] ⩽ 2.5 × 1 0 7 ) (1 \leqslant op_i \leqslant 3, n\cdot m \cdot \sum_{i = 1}^q [op_i == 3] \leqslant 2.5 \times 10^7) (1⩽opi⩽3,n⋅m⋅∑i=1q[opi==3]⩽2.5×107)
N o t e : [ a = = a ] 值为 1 , [ a = = b ] 值为 0 。 ( a ∈ Z , b ∈ Z , a ≠ b ) Note : [a == a] 值为 1,[a == b] 值为 0。 (a \in Z, b \in Z, a \neq b) Note:[a==a]值为1,[a==b]值为0。(a∈Z,b∈Z,a=b)
-
输出描述
时刻谨记,你就是黄昏。
- 对于 o p i = = 1 op_i == 1 opi==1 和 o p i = = 2 op_i == 2 opi==2 ,输出一行一个整数表示答案。
- 对于 o p i = = 3 op_i == 3 opi==3,输出 n n n行 m m m列表示此时敌友方阵, 0 0 0代表友军, 1 1 1代表敌军。
样例输入
3 3 6
111
001
101
1 1 1 1
1 2 1 1
2 0
2 1
1 2 3 0
3
样例输出
2
3
1
2
0
101
110
011
样例解释
初始方阵为: [ 1 1 1 0 0 1 1 0 1 ] \left[ \begin{matrix} 1 & 1 & 1 \\ 0 & 0 & 1 \\ 1 & 0 & 1 \end{matrix} \right] ⎣ ⎡101100111⎦ ⎤
第一次操作为修正,将第一行第一列修正为 1 1 1,方阵不变,此时对第一行和第三列进行攻击是最少的攻击次数,可以在不误伤友军情况下,清理最多的敌军。所以答案为 2 2 2。
第二次同样为修正,将第二行第一列修正为 1 1 1,方阵变为 [ 1 1 1 1 0 1 1 0 1 ] \left[ \begin{matrix} 1 & 1 & 1 \\ 1 & 0 & 1 \\ 1 & 0 & 1 \end{matrix} \right] ⎣ ⎡111100111⎦ ⎤,此时可以对第一行、第一列、第三列进行攻击即为答案 3 3 3。
第三次操作表示有友军由左上角进入,右下角将有人离开,方阵变为 [ 0 1 1 1 1 0 1 1 0 ] \left[ \begin{matrix} 0 & 1 & 1 \\ 1 & 1 & 0 \\ 1 & 1 & 0 \end{matrix} \right] ⎣ ⎡011111100⎦ ⎤,此时答案为 1 1 1。
第四次操作方阵变为 [ 1 0 1 1 1 1 0 1 1 ] \left[ \begin{matrix} 1 & 0 & 1 \\ 1 & 1 & 1 \\ 0 & 1 & 1 \end{matrix} \right] ⎣ ⎡110011111⎦ ⎤,答案为 2 2 2。
第五次操作方阵变为 [ 1 0 1 1 1 0 0 1 1 ] \left[ \begin{matrix} 1 & 0 & 1 \\ 1 & 1 & 0 \\ 0 & 1 & 1 \end{matrix} \right] ⎣ ⎡110011101⎦ ⎤,此时无论攻击哪一行哪一列,均会误伤友军,故答案为0。
第六次操作将此时方阵输出即可。
题解
由题意可知,对于操作后方阵全为 1 1 1时,答案为行数列数中较小的一个。
当操作后方阵不全为1时,记录行中所有列为 1 1 1及列中所有行为 1 1 1的数量,两者之和即为答案。
一开始我们将所给方阵保存下来,并用数组记录每一行列中 1 1 1的数量,算出当前答案。
对于操作 1 1 1:直接修改指定位置即可,只需要变更当前行列的记录值,并更改答案输出。每次操作复杂度 O ( 1 ) O(1) O(1)。
对于操作2:我们发现只是将前 m − 1 m - 1 m−1列全部往右移动了一列,也就是说,实际上只有 1 1 1列的答案和 n n n行的答案受到影响。因此,我们用一个值 n o w now now表示当前第一列所对应的下标(下标与列数开始分离),每次操作将 n o w now now加上 m − 1 m-1 m−1并模 m m m,我们惊奇地发现 n o w % m 、 ( n o w + 1 ) % m 、 ( n o w + 2 ) % m . . . ( n o w + m − 1 ) % m now\% m、(now + 1) \% m、(now + 2) \% m...(now + m - 1) \% m now%m、(now+1)%m、(now+2)%m...(now+m−1)%m,可以完美表示所有列下标。
变为
**所以,每次操作 2 2 2,将最后一列上所有元素向下移动一行,删除最后一列最后一行元素,并在最后一列第一行加入所给操作元素,再将最后一列变为第一列,更改答案输出即可。**每次操作复杂度 O ( n ) O(n) O(n)。
对于操作 3 3 3:输出当前方阵状态即可,注意也需用到 n o w now now映射。每次操作复杂度 O ( n ⋅ m ) O(n \cdot m) O(n⋅m),题目保证操作 3 3 3总复杂度 n ⋅ m ⋅ ∑ i = 1 q [ o p i = = 3 ] ⩽ 2.5 × 1 0 7 n\cdot m \cdot \sum_{i = 1}^q [op_i == 3] \leqslant 2.5 \times 10^7 n⋅m⋅∑i=1q[opi==3]⩽2.5×107
因此总复杂度为 O ( q ⋅ n ) + 2.5 × 1 0 7 O(q \cdot n) + 2.5 \times 10^7 O(q⋅n)+2.5×107
标程
/*----------------------------------*/
/* Author : KaMtuo */
/* Email : 396631483@qq.com */
/* Creation_time : 2022-05-02 15:52 */
/* Software : Visual Studio Code */
/*----------------------------------*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define endl "\n"
using std::cin;
using std::cout;
using std::min;
using std::string;
const int N = 5123;
int n, m, q;
int ans;
int r[N], c[N];
char mp[N][N];
int now;
int main() {
cin >> n >> m >> q;
for (int i = 0; i < n; i ++) scanf("%s", mp[i]);
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
r[i] = r[i] + mp[i][j] - '0';
c[j] = c[j] + mp[i][j] - '0';
}
}
for (int i = 0; i < n; i ++) {
ans += r[i] == m;
}
for (int i = 0; i < m; i ++) {
ans += c[i] == n;
}
while (q --) {
int op;
cin >> op;
if (op == 3) {
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
cout << mp[i][(j + now) % m];
}
cout << endl;
}
} else {
if (op == 1) {
int x, y, z;
cin >> x >> y >> z;
x --;
y --;
y = y + now;
y %= m;
if (mp[x][y] - '0' != z) {
ans -= (r[x] == m) + (c[y] == n);
if (z) r[x] ++, c[y] ++;
else r[x] --, c[y] --;
mp[x][y] = z + '0';
ans += (r[x] == m) + (c[y] == n);
}
} else {
int z;
cin >> z;
int last = (now + (m - 1)) % m;
ans -= c[last] == n;
c[last] = 0;
for (int i = n - 1; i; i --) {
ans -= (r[i] == m);
ans += (r[i] = (r[i] - mp[i][last] + '0' + mp[i - 1][last] - '0')) == m;
mp[i][last] = mp[i - 1][last];
c[last] += mp[i][last] - '0';
}
ans -= (r[0] == m);
ans += (r[0] = ((r[0] - mp[0][last] + '0' + z))) == m;
mp[0][last] = z + '0';
c[last] += mp[0][last] - '0';
ans += c[last] == n;
now = last;
now %= m;
}
cout << (ans == n + m ? min(n, m) : ans) << endl;
}
}
return 0;
}