BZOJ4972 八月月赛 Problem B 小Q的方格纸 二维前缀和
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ4972 八月月赛Problem B
题目概括
一个矩阵,一坨询问,问矩阵中一个特定方向的等腰直角三角形范围的sum。
题解
一开始毫无头绪。
看完9题,一题也不会。
发现这题A的人多,于是我花了15分钟仔细思考。
发现可以了。
对于一个三角形区域,我们可以看下图:
我们把求右下黑色三角形区域转化成一个矩形和3个左上的三角形,然后就OK了。
矩形只要前缀和就可以了,O(nm)
求贴在上面和左边的,各自只要O(nm)
询问O(1)
代码
#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; }