Description
给出一个长度为 n n 的序列,用以下代码生成一个无限大的矩阵
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;
}
}
q q 次查询,每次查询一个子矩阵的元素和
Input
第一行一整数表示用例组数,每组用例首先输入一整数 n n 表示序列长度,之后输入 n n 个整数表示该序列,之后输入一整数 q q 表示查询数,最后行每行输入四个整数 x0,y0,x1,y1 x 0 , y 0 , x 1 , y 1 表示查询子矩阵 [x0,x1]×[y0,y1] [ x 0 , x 1 ] × [ y 0 , y 1 ] 元素和
(1≤T≤100.1≤n≤10,1≤Ai≤100,1≤q≤100,0≤x0≤x1≤108,1≤y0≤y1≤108) ( 1 ≤ T ≤ 100.1 ≤ n ≤ 10 , 1 ≤ A i ≤ 100 , 1 ≤ q ≤ 100 , 0 ≤ x 0 ≤ x 1 ≤ 10 8 , 1 ≤ y 0 ≤ y 1 ≤ 10 8 )
Output
输出查询子矩阵的元素和
Sample Input
1
3
1 10 100
5
3 3 3 3
2 3 3 3
2 3 5 8
5 1 10 10
9 99 999 1000
Sample Output
1
101
1068
2238
33076541
Solution
假设所生成无限矩阵行列从 1 1 开始,那么第行第 j j 列的元素编号应为,根据这个显然知道 B(i,j)=B(2sn+i,2tn+j) B ( i , j ) = B ( 2 s n + i , 2 t n + j ) ,故左上角 2n⋅2n 2 n ⋅ 2 n 的小矩阵是整个大矩阵的循环节,预处理出该小矩阵之后即可快速求出左上角开始的任意一个大矩阵的和,之后由容斥原理即可得到一个子矩阵的和
Code
#include<cstdio>
using namespace std;
typedef long long ll;
#define y2 yy2
int T,n,q,A[100],s[21][21];
ll Solve(int x,int y)
{
ll ans=(ll)(x/n)*(y/n)*s[n][n];
ans+=(ll)(x/n)*s[n][y%n]+(ll)(y/n)*s[x%n][n]+s[x%n][y%n];
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&A[i]);
int res=0;
for(int i=1;i<=4*n+1;i++)
for(int j=1;j<=i;j++)
{
if(j<=2*n&&i-j+1<=2*n)s[j][i-j+1]=A[res];
res=(res+1)%n;
}
n*=2;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
scanf("%d",&q);
while(q--)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%lld\n",Solve(x2+1,y2+1)+Solve(x1,y1)-Solve(x1,y2+1)-Solve(x2+1,y1));
}
}
return 0;
}