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;
}

  

posted @ 2017-08-20 23:08  zzd233  阅读(584)  评论(0编辑  收藏  举报