题意
给你一个由长度为L的数组A构造的无限大矩阵M,矩阵中的数构造方式如下
int cursor = 0; for (int i = 0; ; ++i) { for (int j = 0; j <= i; ++j) { M[j][i - j] = A[cursor]; cursor = (cursor + 1) % L; } }
输入有T个样例,每个样例一个L,和L个数A[i](0<i<L),Q次询问,
问左上角(xL,yL)到右下角(xR,yR)这个矩形区域内所有M[i][j]的和。
题解
假设L = 7,那么M矩阵构造如下(从右上至左下以7为循环赋值)
A[0]A[1]A[3]A[6]
A[2]A[4]A[0]
A[5]A[1]
A[2]
找规律发现M[i][j] = M[i][j + 2L] = M[i + 2L][j]。所以大矩阵是由很多个以2L为边长的正方形构成的。
如何求矩阵呢,如下图一要求绿色区域的面积那么就是黑色区域面积减去两个灰色区域面积再加上一个红色区域面积。
四个矩形都有一个固定的求解方法calc(int n, int m)。
这四个矩形都是从(0,0)到(n,m)。
先将M矩阵在2*L范围内求一个前缀和M[y][x]表示的是(0,0)到(y,x)矩形内值的总和。
下面图二是cal(n, m)的模拟计算
红色区域是 (2L * (n / L), 2L * (m / L))
绿色区域是 (n%2L, 2L*(m/2L))和(2L*(n/2L), m%2L)
蓝色区域是 (n%2L, m%2L)
具体情况见代码
代码
#include<cstdio>
#include <iostream>
#include<algorithm>
#include<string.h>
#include <string.h>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
int A[10], M[100][100];
int T, L, Q;
ll calc(int n, int m)
{
//此时L是2倍L
if(n < 0 || m < 0) return 0;
return 1ll*M[L-1][L-1]*(n/L)*(m/L)+ //红色
1ll*M[n%L][L-1]*(m/L)+ //绿色
1ll*M[L-1][m%L]*(n/L)+ //绿色
1LL*M[n%L][m%L]; //蓝色
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &L);
for(int i = 0; i < L; i++)
{
scanf("%d", &A[i]);
}
int cursor = 0;
for (int i = 0; i < 4*L ; ++i)
{
for (int j = 0; j <= i; ++j)
{
//if (j < 2 * L && i - j < 2 * L)
M[j][i - j] = A[cursor];
cursor = (cursor + 1) % L;
}
}
L*=2;
for(int i = 0; i < L; i++)
for(int j = 0; j < L; j++)
{
if(i) M[i][j] += M[i-1][j];
if(j) M[i][j] += M[i][j-1];
if(i&&j) M[i][j] -= M[i-1][j-1];
}
scanf("%d", &Q);
while(Q--)
{
int xL, yL, xR, yR;
scanf("%d%d%d%d", &xL, &yL, &xR, &yR);
//图1 黑色-灰色-灰色+红色
printf("%I64d\n", calc(xR,yR)-calc(xL-1, yR)-calc(xR,yL-1)+calc(xL-1,yL-1));
}
}
return 0;
}