HDU

题意:

for (int i = 0; ; ++i) {
for (int j = 0; j <= i; ++j) {
M[j][i - j] = A[cursor];
cursor = (cursor + 1) % L;
}
}

 给定序列A[1..L],二维数组M的规律由以上代码给出。Q个查询,每次给x0,y0,x1,y1 (0x0x11e8,0y0y11e8)四个数,求以(x0,y0)和(x1,y1)两个点为端点的矩形中数的和。

分析:根据推导可得,M[i][j] = M[i+2L][j] + M[i][j+2L]。当L为奇数时,每个L*L的块都是一个循环节;L为偶数时,每个2*L*2*L是一个循环节,可以打表验证。

预处理出M[i][j]一个循环节的的二维前缀和,复杂度O(L^2)。每次计算答案可以由前缀和相加减所得:S(x1,y1)  - S(x1,y0-1) - S(x0-1, y1) + S(x0-1,y0-1)。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e2+5;
LL M[maxn][maxn];
int A[maxn];
int L;

LL area(int x,int y){
    LL block = M[L-1][L-1];         //一个块的和
    LL res=block *(x/L)*(y/L);
    res+=M[x%L][y%L];
    res+=(x/L)*M[L-1][y%L];
    res+=(y/L)*M[x%L][L-1];
    return res;
}

int main()
{
    #ifndef ONLINE_JUDGE
         freopen("in.txt","r",stdin);
         freopen("out.txt","w",stdout);
    #endif
    int T,N,Q,u,v,tmp,K;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&N);
        for(int i=0;i<N;++i)
            scanf("%d",&A[i]);
        if(N&1) L =N*2;
        else L = (N<<2); 
        int cur = 0;
        for(int i=0;i<L;++i){
            for(int j=0;j<=i;++j){
                M[j][i-j] = A[cur];
                cur  = (cur+1) %N;
            }
        }
        L/=2;
        for(int i=0;i<L;++i){
            for(int j=0;j<L;++j){
                M[i][j] +=M[i][j-1];
                M[i][j] +=M[i-1][j];
                M[i][j] -=M[i-1][j-1];
            }
        }
        scanf("%d",&Q);
        while(Q--){
            int a,b,c,d;
            scanf("%d%d%d%d",&a,&b,&c,&d);
            LL res = area(c,d)+area(a-1,b-1)-area(c,b-1)-area(a-1,d);
            printf("%lld
",res);
        }
    }
    return 0;
}
为了更好的明天
原文地址:https://www.cnblogs.com/xiuwenli/p/9406411.html