[BZOJ 2738] 矩阵乘法 · 分块

3 篇文章 0 订阅

标算整体二分,然而窝太弱了并不会做。

分块大法好:狠狠点我

我们把n*n个数排序,然后从小到大插入矩阵,每次插n个,用前缀和维护每个子矩阵当前已经填了多少个数。

查找的时候 对于每个询问,如果子矩阵里的数已经超过了k个,说明答案在当前插入的这n个数里,倒着查找即可。

用链表维护询问,已经出解的直接跳过。

因为每个询问最多会查n次,所以复杂度O(nq),然而如果用树状数组代替前缀和维护的话是O(nqlog^2),妥妥T。

#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;

const int N=505;
struct arr{
	int x,y,num;
	bool operator <(const arr &A)const
	{return num<A.num;}
}data[N*N];
struct typ{
	int x1,x2,y1,y2,k;
}query[60005];
int n,m,t[N][N],c[N][N],x,i,j,k,p,s,ans[60005];
int pre[N*N],suf[N*N];
int x1,x2,y1,y2;

int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
		for (j=1;j<=n;j++){
			scanf("%d",&x);
			data[i*n-n+j].x=i;
			data[i*n-n+j].y=j;
			data[i*n-n+j].num=x;
		}
	sort(data+1,data+n*n+1);
	for (i=1;i<=m;i++){
		scanf("%d%d%d%d%d",&query[i].x1,&query[i].y1,&query[i].x2,&query[i].y2,&query[i].k);
		suf[i]=i+1;
		pre[i]=i-1;
	}
	suf[0]=1;
	
	memset(t,0,sizeof t);
	for (i=1;i<=n;i++){
		int q=i*n-n+1,w=i*n;
		for (j=q;j<=w;j++)
			c[data[j].x][data[j].y]=1;
		for (j=1;j<=n;j++)
			for (k=1;k<=n;k++)
				t[j][k]=c[j][k]+t[j-1][k]+t[j][k-1]-t[j-1][k-1];
		
		for (j=suf[0];j<=m;j=suf[j]){
			x1=query[j].x1;x2=query[j].x2;y1=query[j].y1;y2=query[j].y2;
			s=t[x2][y2]+t[x1-1][y1-1]-t[x1-1][y2]-t[x2][y1-1];
			if (s<query[j].k) continue;
			for (p=w;p>=q;p--)
				if (data[p].x>=x1 && data[p].x<=x2 
					&& data[p].y>=y1 && data[p].y<=y2) 
						if (s==query[j].k) {
								ans[j]=data[p].num;
								suf[pre[j]]=suf[j];
								pre[suf[j]]=pre[j];
								break;
							}
							else s--;
		}
	}
	for (i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值