[BZOJ 2738] 矩阵乘法 【分块】

题目链接:BZOJ - 2738

 

题目分析

题目名称 “矩阵乘法” 与题目内容没有任何关系..就像VFK的 A+B Problem 一样..

题目大意是给定一个矩阵,有许多询问,每次询问一个子矩阵中的第 k 小值。

我看了神犇的题解,使用一种非常神奇的做法:

将矩阵中的数排个序,从小到大填到矩阵中。每次填 Size 个(这里就是分块)。

然后每填完一次,就暴力重新求一下 Sum[][] (二维前缀和), 然后枚举每个询问,看看这个询问的子矩形内已经填入的数是否不少于询问的 k 。

如果子矩形内已填入的数不少于询问的 k ,那么这个询问的答案一定就是在这一次填入的 Size 个数中,于是就枚举这 Size 个数,然后判断每个数是不是在子矩形内,就可以求出刚好填到第 k 个是哪个数了。

然后就可以将这次询问删掉了,用双向链表实现删除一个询问。

复杂度是 O(n^4 / Size + n^2 * q / Size + q * Size) ,然后用均值不等式求出一个最优的 Size 就可以了。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

typedef double LF;

inline int gmin(int a, int b) {return a < b ? a : b;}

const int MaxN = 500 + 5, MaxM = 60000 + 5;

int n, m, Top, Blk, S, T;
int Sum[MaxN][MaxN], Has[MaxN][MaxN];

struct ENum
{
	int Num, x, y;
} E[MaxN * MaxN];

inline bool Cmp(ENum e1, ENum e2)
{
	return e1.Num < e2.Num;
}

struct EQ
{
	int x, y, xx, yy, k, Ans, Prev, Next;
	
	bool Inside(ENum e)
	{
		return (e.x >= x && e.x <= xx) && (e.y >= y && e.y <= yy);
	}
} Q[MaxM];

inline int GetSum(int x, int y, int xx, int yy)
{
	return Sum[xx][yy] - Sum[x - 1][yy] - Sum[xx][y - 1] + Sum[x - 1][y - 1];
}

int main()
{
	scanf("%d%d", &n, &m);
	Blk = (int)((LF)n * (1.0 + ((LF)n / sqrt((LF)m)))) + 1;
	Top = 0;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
		{
			scanf("%d", &E[++Top].Num);
			E[Top].x = i; E[Top].y = j;
		}
	sort(E + 1, E + Top + 1, Cmp);	
	for (int i = 1; i <= m; ++i)
		scanf("%d%d%d%d%d", &Q[i].x, &Q[i].y, &Q[i].xx, &Q[i].yy, &Q[i].k);
	for (int i = 1; i < m; ++i) Q[i].Next = i + 1;
	for (int i = 2; i <= m; ++i) Q[i].Prev = i - 1;
	S = m + 1; T = m + 2;
	Q[S].Next = 1; Q[1].Prev = S;
	Q[m].Next = T; Q[T].Prev = m;
	int p;
	for (int i = 1; i <= Top; i += Blk)
	{
		p = gmin(i + Blk - 1, Top);
		for (int j = i; j <= p; ++j)
			Has[E[j].x][E[j].y] = 1;
		for (int j = 1; j <= n; ++j)
			for (int k = 1; k <= n; ++k)
				Sum[j][k] = Sum[j - 1][k] + Sum[j][k - 1] - Sum[j - 1][k - 1] + Has[j][k];
		int j = Q[S].Next;
		while (j != T)
		{
			int CntNow = GetSum(Q[j].x, Q[j].y, Q[j].xx, Q[j].yy);
			if (CntNow >= Q[j].k)
			{
				int Pos = p;
				while (CntNow >= Q[j].k)
				{
					if (Q[j].Inside(E[Pos])) --CntNow;
					--Pos;
				}
				Q[j].Ans = E[Pos + 1].Num;
				Q[Q[j].Next].Prev = Q[j].Prev;
				Q[Q[j].Prev].Next = Q[j].Next;		
			}
			j = Q[j].Next;
		}
	}
	for (int i = 1; i <= m; ++i) printf("%d\n", Q[i].Ans);
	return 0;
}

  

posted @ 2015-06-05 16:35  JoeFan  阅读(389)  评论(0编辑  收藏  举报