E. Vus the Cossack and a Field (规律题)

题目链接:哆啦A梦传送门

题意:给出原始n行m列矩阵A,他的反置矩阵为B(与A相对应的位置取反)。

原始矩阵 : A
第一次可以变成: [ A B B A ] \begin{bmatrix} A&B \\ B& A \end{bmatrix} [ABBA]
以此类推。
现在有q个询问,每次询问左上角为(x1,y1),右下角为(x2,y2)的矩阵的总和。

我们可以发现N行M列的矩阵A+B=N*M,这个很容易可以得到。
再者,我们观察题目给的数据:
[ 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 ] \begin{bmatrix} 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{bmatrix} 1100001101101001001111001001011000111100100101101100001101101001

可以发现:相邻的奇数行与偶数行,技术列与偶数列对应的点值异或为1。
那么我们就可以解决了。
我们设 f ( x , y ) f(x,y) f(x,y)表示为左上角为(1,1),右下角为(x,y)的矩阵和。
那么此题要求的结果就为:
s u m = f ( x 2 , y 2 ) − f ( x 1 − 1 , y 2 ) − f ( x 2 − 1 , y 1 ) + f ( x 1 , y 1 ) sum=f(x2,y2)-f(x1-1,y2)-f(x2-1,y1)+f(x1,y1) sum=f(x2,y2)f(x11,y2)f(x21,y1)+f(x1,y1)

这是我们先让原始矩阵变换为下一个矩阵,就变为2n2m的矩阵了(n<<=1,m<<=1)。
此时对于求 f ( x , y ) f(x,y) f(x,y),因为我们已经知道n
m的矩阵值,故我们可以看看在范围为(1,1)->(x,y)中有多少个为n*m的矩阵块,这些值都是固定的,那么此时还剩下a=x%n,b=y%m,也就是a行m列,n行b列以及a行b列。

根据我们前面说的发现,a行m列,n行b列的值其实也固定了(就为一半),为什么呢?
我们可以知道总点(1,1)到(a,m)以及(n,b)如果c与d不全为奇数,那么此时这个(c,d)矩阵和就为 c ∗ d 2 \frac{c*d}{2} 2cd,这肯定是显然的,因为一开始我们就变换了一次矩阵,是的边长都扩大为2倍,即n,m都为偶数。即现在的结果为 x ∗ y − a ∗ b 2 \frac{x*y-a*b}{2} 2xyab(没算(a,b))。

那么现在就剩下a行b列没求了,此时我们可以预处理出n行m列的 f ( x , y ) ∣ x &lt; = n , y &lt; = m f(x,y)|x&lt;=n,y&lt;=m f(x,y)x<=n,y<=m
但我们还需要知道此时a行b列的矩阵是否是取反的矩阵,

这里有个技巧:
b i t c n t ( x ) bitcnt(x) bitcnt(x)为x的二进制表示下1的数量,若bitcnt(x/n)+bitcnt(y/m)为奇数,则(x,y)处于矩阵B中,否则(x,y)处于矩阵A中。
证明:

假设处于矩阵A,那么直接加上预处理的结果,否则a*b-预处理的结果。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;

typedef long long LL;

const int N=1010;

int num[N][N];
int   n,m;
int q;
LL solve(LL x,LL y)
{
    if(x<=0||y<=0) return 0;
    LL a=x%n,b=y%m;
   LL sum=(x*y-a*b)/2;
   if(((__builtin_popcountll(x/n))^(__builtin_popcountll(y/m)))&1){
    sum=sum+(a*b-num[a][b]);
   }
   else sum+=num[a][b];


   return sum;

}

int main()
{


    cin>>n>>m>>q;

    for(int i=1;i<=n;i++)
    {
        string s;
        cin>>s;
        for(int j=1;j<=m;j++){
                int x=s[j-1]-'0';
            num[i][j]=num[i+n][j+m]=x;
            num[i+n][j]=num[i][j+m]=x^1;
        }
    }

    n<<=1;
    m<<=1;

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++){
            num[i][j]+=(num[i-1][j]+num[i][j-1]-num[i-1][j-1]);
        }
    }


    while(q--)
    {
       LL x1,y1,x2,y2;
        scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
        printf("%lld\n",solve(x2,y2)-solve(x1-1,y2)-solve(x2,y1-1)+solve(x1-1,y1-1));
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值