BZOJ4972 八月月赛 Problem B 小Q的方格纸 二维前缀和

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


 题目传送门 - BZOJ4972 八月月赛Problem B


 题目概括

  一个矩阵,一坨询问,问矩阵中一个特定方向的等腰直角三角形范围的sum。


 题解

  一开始毫无头绪。

  看完9题,一题也不会。

  发现这题A的人多,于是我花了15分钟仔细思考。

  发现可以了。

  对于一个三角形区域,我们可以看下图:

  

  我们把求右下黑色三角形区域转化成一个矩形和3个左上的三角形,然后就OK了。

  矩形只要前缀和就可以了,O(nm)

  求贴在上面和左边的,各自只要O(nm)

  询问O(1)


 

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef unsigned int uint;
const int N=3000+5;
const uint Inv=534566745;
uint A,B,C,x,y,k,t,a[N][N],sum[N][N],L[N][N*2],U[N][N];
int n,m,q;
uint rng61(){
    A^=A<<16,A^=A>>5,A^=A<<1,t=A,A=B,B=C,C^=t^A;
    return C;
}
uint Pow(uint x,int y){
    if (y==0)
        return 1;
    uint xx=Pow(x,y/2);
    xx*=xx;
    if (y&1)
        xx*=x;
    return xx;
}
uint val(int xa,int ya,int xb,int yb){
    xb=min(xb,n),yb=min(yb,m);
    return sum[xb][yb]-sum[xb][ya-1]-sum[xa-1][yb]+sum[xa-1][ya-1];
}
int main(){
    scanf("%d%d%d%u%u%u",&n,&m,&q,&A,&B,&C);
    memset(sum,0,sizeof sum);
    memset(L,0,sizeof L);
    memset(U,0,sizeof U);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            a[i][j]=rng61();
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
        }
    for (int i=n;i>=1;i--)
        for (int j=1;j<=m*2;j++)
            L[i][j]=L[i+1][j-1]+val(i,1,i,j);
    for (int i=m;i>=1;i--)
        for (int j=1;j<=n;j++)
            U[i][j]=U[i+1][j-1]+val(1,i,j,i);
    uint timesx=Pow(233,q),Ans=0;
    for (int i=1;i<=q;i++){
        timesx*=Inv;
        x=rng61()%n+1,y=rng61()%m+1,k=rng61()%min(x,y)+1;
        int xx=x,yy=y,kk=k;
        uint ans=sum[x][y]-L[1][x+y-k-1]+L[x+1][max(0,yy-kk-1)]+U[y+1][max(0,xx-kk-1)];
        Ans+=timesx*ans;
    }
    printf("%u",Ans);
    return 0;
}

  

posted @   zzd233  阅读(587)  评论(0编辑  收藏  举报
努力加载评论中...

点击右上角即可分享
微信分享提示