@codeforces - [email protected] Vus the Cossack and a Field

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下:

(1)将这个矩阵写在左上角。
(2)将这个矩阵每位取反写在右上角。
(3)将这个矩阵每位取反写在左下角。
(4)将这个矩阵写在右下角。
(5)将得到的矩阵再作为初始矩阵,重复这些操作。

比如对于初始矩阵:
\[\begin{matrix} 1 & 0 & \\ 1 & 1 & \\ \end{matrix}\]

它操作一次变为:
\[\begin{matrix} 1 & 0 & 0 & 1 \\ 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 0 \\ 0 & 0 & 1 & 1 \\ \end{matrix}\]

再操作一次变为:
\[\begin{matrix} 1 & 0 & 0 & 1 & 0 & 1 & 1 & 0 \\ 1 & 1 & 0 & 0 & 0 & 0 & 1 & 1 \\ 0 & 1 & 1 & 0 & 1 & 0 & 0 & 1 \\ 0 & 0 & 1 & 1 & 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 0 & 1 & 0 & 0 & 1 \\ 0 & 0 & 1 & 1 & 1 & 1 & 0 & 0 \\ 1 & 0 & 0 & 1 & 0 & 1 & 1 & 0 \\ 1 & 1 & 0& 0 & 0 & 0 & 1 & 1 \\ \end{matrix}\]

然后继续操作……

我们将左上角记作 (1, 1)。
现在进行 q 次询问,每次询问以 (x1, y1) 为左上角,(x2, y2) 为右下角的矩阵内所有元素的和。

input
第一行包含三个整数 n, m, q (1 <= n, m <= 1000, 1 <= q <= 10^5)。
接下来 n 行每行包含 m 个字符 cij,描述初始矩阵。
接下来 q 行每行包含四个整数 x1, y1, x2, y2,描述一个询问。

output
对于每个询问,输出答案。

sample input 1
2 2 5
10
11
1 1 8 8
2 4 5 6
1 2 7 8
3 3 6 8
5 6 7 8
sample output 1
32
5
25
14
4

sample input 2
2 3 7
100
101
4 12 5 17
5 4 9 4
1 4 13 18
12 1 14 9
3 10 7 18
3 15 12 17
8 6 8 12
sample output 2
6
3
98
13
22
15
3

@[email protected]

要用递归打败递归!

考虑将一个询问拆成前缀和作差的形式,则询问相当于转换为 (1, 1) 到 (x, y) 的前缀和。
如果直接递归分治求解显然就直接炸了,所以我们要发现一些性质。

我们不妨记初始矩阵为 M0,操作一次后的矩阵为 M1,操作两次后的矩阵为 M2,……,以此类推。
观察题目中给出的样例 M1, M2,不难发现每一行 1 的个数都相等,且都等于这一行元素总数的一半;列同理。
这样我们可以快速求解出 Mi 所有元素的和,并求解出 Mi 前若干行/列所有元素的和(这里 i = 0 要特殊处理,因为没有这个性质)

设 Mi 包含 (x, y),考虑 (x, y) 位置:
(1)在 Mi 左上部分,直接递归到 M(i-1)。
(2)在 Mi 右上部分,此时询问包含两部分:在左上部分有一些完整的行,在右上部分是一个规模较小的子问题。左上直接用公式算,右上递归。
(3)在 Mi 左下部分,大致同(2)的处理。
(4)在 Mi 右下部分,一样考虑左上、左下、右上、右下分别是什么形状,发现只有右下是跟原题类似的,规模较小的子问题。然后大概同(2)的处理。
一样当 i = 0 要特殊处理。

发现每一次都只会向下递归一次,递归一次规模至少减半,所以一次询问是 O(log) 的时间复杂度。

具体可能代码更具说服力。

@accepted [email protected]

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1000;
ll s[MAXN + 5][MAXN + 5];
int n[MAXN + 5], m[MAXN + 5];
char c[MAXN + 5];
ll fun1(int dep, int x, int y) {
    if( dep == -1 ) return s[x][y];
    if( x <= n[dep] && y <= m[dep] )
        return fun1(dep - 1, x, y);
    if( x <= n[dep] && y > m[dep] ) {
        if( dep ) return (1LL*x*(y-m[dep]) - fun1(dep - 1, x, y-m[dep])) + 1LL*x*m[dep-1];
        else return (1LL*x*(y-m[dep]) - fun1(dep - 1, x, y-m[dep]) + fun1(dep - 1, x, m[dep]));
    }
    if( x > n[dep] && y <= m[dep] ) {
        if( dep ) return (1LL*(x-n[dep])*y - fun1(dep - 1, x-n[dep], y)) + 1LL*n[dep-1]*y;
        else return (1LL*(x-n[dep])*y - fun1(dep - 1, x-n[dep], y) + fun1(dep - 1, n[dep], y));
    }
    if( x > n[dep] && y > m[dep] ) {
        //puts("?");
        if( dep ) {
            //printf("? %lld %lld %lld\n", fun2(dep - 1, x-n[dep]), fun3(dep - 1, y-m[dep]), 1LL*n[dep]*m[dep]/2);
            return fun1(dep - 1, x-n[dep], y-m[dep]) + 1LL*(x-n[dep])*m[dep-1] + 1LL*n[dep-1]*(y-m[dep]) + 1LL*n[dep]*m[dep]/2;
        }
        else {
            //printf("| %lld %lld %lld %lld\n", 1LL*x*y, (1LL*(x-n[dep])*(y-m[dep]) - fun1(dep - 1, x-n[dep], y-m[dep])), fun1(dep - 1, x-n[dep], m[dep]) + fun1(dep - 1, n[dep], y-m[dep]), s[n[0]][m[0]]);
            return 1LL*(x-n[dep])*m[dep] + 1LL*n[dep]*(y-m[dep]) + fun1(dep - 1, x-n[dep], y-m[dep]) + s[n[0]][m[0]] - fun1(dep - 1, x-n[dep], m[dep]) - fun1(dep - 1, n[dep], y-m[dep]);
        }
    }
}
ll sum(int x, int y) {
    if( x == 0 || y == 0 ) return 0;
    int dep; for(dep = 0; x - n[dep] > n[dep] || y - m[dep] > m[dep]; dep++);
    return fun1(dep, x, y);
}
int main() {
    int q;
    scanf("%d%d%d", &n[0], &m[0], &q);
    for(int i=1;i<=n[0];i++) {
        scanf("%s", c + 1);
        for(int j=1;j<=m[0];j++)
            s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + (c[j] - '0');
    }
    for(int i=1;i<=MAXN;i++)
        n[i] = 2*n[i-1], m[i] = 2*m[i-1];
    for(int i=1;i<=q;i++) {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%lld\n", sum(x2, y2) - sum(x1-1, y2) - sum(x2, y1-1) + sum(x1-1, y1-1));
    }
}

@[email protected]

好久没打 CF 了……活动活动手脚。
不过被 B 题坑了。。。一个假题做了我将近 1 个小时。
大概想着我可能要掉成灰了,这时 CF 突然发表通知,说 B 题标算有误,然后整道题就凭空消失了。还好最后 unrated 了,不然亏死 www。

这道题不难,但非常有 CF 的风格。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11105345.html

时间: 2024-08-02 08:01:46

@codeforces - [email protected] Vus the Cossack and a Field的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m

@codeforces - [email&#160;protected] Koala and Notebook

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n 点 m 边的无向连通图,每条边的编号按照输入顺序依次为 1, 2, ..., m. 现从 1 号点出发,当经过编号为 i 的边时,将 i 写下来.因为写的数之间没有空隙,所以写下来的所有数最终会连成一个数. 对于每一个除 1 以外的点,当它作为终点时,最终连成的数最小是多