长沙学院20级训练赛-G题题解(间谍过家家)

间谍过家家

比赛链接

邀请码: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) (1nmq5000)

    • 第二行到第 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) (1iq)

      • 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) (1xn,1ym,0c1)
      • o p i = = 2 op_i == 2 opi==2 :其后紧随一个用空格隔开的整数 c c c,表示有一个 c c c人员由左上角进入训练基地。 ( 0 ⩽ c ⩽ 1 ) (0 \leqslant c\leqslant 1) (0c1)
      • 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) (1opi3,nmi=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(aZ,bZ,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 m1列全部往右移动了一列,也就是说,实际上只有 1 1 1列的答案和 n n n行的答案受到影响。因此,我们用一个值 n o w now now表示当前第一列所对应的下标(下标与列数开始分离),每次操作将 n o w now now加上 m − 1 m-1 m1并模 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+m1)%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(nm),题目保证操作 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 nmi=1q[opi==3]2.5×107

因此总复杂度为 O ( q ⋅ n ) + 2.5 × 1 0 7 O(q \cdot n) + 2.5 \times 10^7 O(qn)+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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值