bzoj2738矩阵乘法

Description

  给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

Input

 
  第一行两个数N,Q,表示矩阵大小和询问组数;
  接下来N行N列一共N*N个数,表示这个矩阵;
  再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

Output

  对于每组询问输出第K小的数。

Sample Input

2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3

Sample Output

1
3

HINT

  矩阵中数字是109以内的非负整数;

  20%的数据:N<=100,Q<=1000;

  40%的数据:N<=300,Q<=10000;

  60%的数据:N<=400,Q<=30000;

  100%的数据:N<=500,Q<=60000。


据说正解是整体二分,然而看了半天并没有看懂,最后选择了分块
首先将矩阵里的数字排序,然后每次插size个,插入后暴力扫一遍询问,如果有询问的区间在本次操作中数字个数大于了k,就说明查询的k值在刚刚插入的
size个数中,此时再暴力倒序扫一遍刚刚的size个数字就好了,二位前缀和维护矩阵内数字的个数,链表维护询问即可
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#define maxn 505
#define maxq 60005
using namespace std;
inline void read(int& x)
{	char c=getchar();x=0;int y=1;
	while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	x*=y;
}
template<typename T>inline T m_min(T x,T y){return x<y?x:y;}
int n,q,sum[maxn][maxn],sz,ans[maxq],done,cnt,pre[maxq],nex[maxq],A[maxn][maxn];
struct query
{	int x1,x2,y1,y2,k;
	query(int a=0,int b=0,int c=0,int d=0,int e=0):x1(a),x2(b),y1(c),y2(d),k(e){}
}Q[maxq];
struct node
{	int x,y,v;
	node(int x=0,int y=0,int z=0):x(x),y(y),v(z){}
	friend bool operator<(const node& a,const node& b){return a.v<b.v;}
}matrix[maxn*maxn];
int main()
{	read(n);read(q);int x,y,a,b,c;
	for(int i=1;i<=n;++i)	
		for(int j=1;j<=n;++j)
		{	read(x);
			matrix[++cnt]=node(i,j,x);
		}
	for(int i=1;i<=q;++i)
	{	read(x);read(y);read(a),read(b);read(c);
		Q[i]=query(x,a,y,b,c);
		pre[i]=i-1;nex[i]=i+1;
	}
	nex[0]=1;
	sort(matrix+1,matrix+cnt+1);
	register int i,j,k,lef,rig,_,__;
	int now;
	for(i=1;i<=n;++i)
	{	lef=(i-1)*n+1;rig=lef+n-1;
		for(j=lef;j<=rig;++j)
			A[matrix[j].x][matrix[j].y]=1;
		for(_=1;_<=n;++_)
			for(__=1;__<=n;++__)
				sum[_][__]=sum[_][__-1]+sum[_-1][__]-sum[_-1][__-1]+A[_][__];
		for(j=nex[0];j<=q;j=nex[j])
		{	now=sum[Q[j].x2][Q[j].y2]-sum[Q[j].x2][Q[j].y1-1]-sum[Q[j].x1-1][Q[j].y2]+sum[Q[j].x1-1][Q[j].y1-1];
			if(now>=Q[j].k)
				for(int l=rig;l>=lef;--l)
					if(matrix[l].x>=Q[j].x1&&matrix[l].x<=Q[j].x2&&matrix[l].y>=Q[j].y1&&matrix[l].y<=Q[j].y2)
					{   --now;
						if(now<Q[j].k)
						{	ans[j]=matrix[l].v;++done;
							nex[pre[j]]=nex[j];pre[nex[j]]=pre[j];
							break;
						}
					}
		}
		if(done==q) break;
	}
	for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>