zoukankan      html  css  js  c++  java
  • @codeforces


    @description@

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

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

    比如对于初始矩阵:

    [egin{matrix} 1 & 0 & \ 1 & 1 & \ end{matrix} ]

    它操作一次变为:

    [egin{matrix} 1 & 0 & 0 & 1 \ 1 & 1 & 0 & 0 \ 0 & 1 & 1 & 0 \ 0 & 0 & 1 & 1 \ end{matrix} ]

    再操作一次变为:

    [egin{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

    @solution@

    要用递归打败递归!

    考虑将一个询问拆成前缀和作差的形式,则询问相当于转换为 (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 code@

    #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
    ", 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
    ", 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
    ", sum(x2, y2) - sum(x1-1, y2) - sum(x2, y1-1) + sum(x1-1, y1-1));
    	}
    }
    

    @details@

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

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

  • 相关阅读:
    lcx
    交换网络中存在的攻击及加固方法概括
    Hello world~
    CCSPSECURE1 安全理论
    SQL注入经验总结
    Access Control List
    初探java集合框架图
    深入浅出分析LinkedHashMap
    红黑树实现分析
    深入浅出的分析TreeMap
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11105345.html
Copyright © 2011-2022 走看看