[BZOJ2738]矩阵乘法

[BZOJ2738]矩阵乘法

试题描述

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

输入

第一行两个数 \(N,Q\),表示矩阵大小和询问组数;

接下来 \(N\)\(N\) 列一共 \(N \times N\) 个数,表示这个矩阵;

再接下来 \(Q\) 行每行 \(5\) 个数描述一个询问:\(x_1,y_1,x_2,y_2,k\) 表示找到以 \((x_1,y_1)\) 为左上角、以 \((x_2,y_2)\) 为右下角的子矩形中的第 \(K\) 小数。

输出

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

输入示例

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

输出示例

1
3

数据规模及约定

矩阵中数字是 \(10^9\) 以内的非负整数;

\(20\texttt{%}\) 的数据:\(N \le 100,Q \le 1000\)

\(40\texttt{%}\) 的数据:\(N \le 300,Q \le 10000\)

\(60\texttt{%}\) 的数据:\(N \le 400,Q \le 30000\)

\(100\texttt{%}\) 的数据:\(N \le 500,Q \le 60000\)

题解

所谓的整体二分,其实就是一种处理离线询问的方法。

\(solve(l, r, S)\) 表示处理答案区间为 \([l, r]\) 的询问集合为 \(S\) 的部分。那么令 \(m = \lfloor \frac{l +r}{2} \rfloor\),对于 \(S\) 中的询问,我们先将 \([l, m]\) 中的数加入二维树状数组中,然后查询每个 \(S\) 中的询问,如果比询问中的 \(k\) 大,则该询问的答案在 \([l, m]\) 中,否则在 \([m + 1, r]\) 中。

注意整体二分的时间复杂度分析,很容易不小心写成 \(O(n^2)\) 的。(本题 \(O(n^2log_2^3n + qlog_2^3n)\)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 510
#define maxq 60010
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)

int n, q, A[maxn][maxn], num[maxn*maxn], ToT, head[maxn*maxn], nxt[maxn*maxn];
pii pos[maxn*maxn];

struct Que {
	int x1, y1, x2, y2, k, id;
	bool type;
	Que() {}
	Que(int _1, int _2, int _3, int _4, int _k, int _id): x1(_1), y1(_2), x2(_3), y2(_4), k(_k), id(_id) {}
} qs[maxq], tqs[maxq];

int C[maxn][maxn];
void Add(int x, int y, int v) {
	for(; x <= n; x += x & -x)
		for(int Y = y; Y <= n; Y += Y & -Y) C[x][Y] += v;
	return ;
}
int que(int x, int y) {
	int sum = 0;
	for(; x; x -= x & -x)
		for(int Y = y; Y; Y -= Y & -Y) sum += C[x][Y];
	return sum;
}
int Query(int x1, int y1, int x2, int y2) {
	return que(x2, y2) - que(x1 - 1, y2) - que(x2, y1 - 1) + que(x1 - 1, y1 - 1);
}

int Ans[maxq];
void solve(int l, int r, int ql, int qr) {
	if(ql > qr) return ;
	if(l == r) {
		rep(i, ql, qr) Ans[qs[i].id] = num[l];
		return ;
	}
	int mid = l + r >> 1;
//	printf("[%d, %d] %d [%d, %d]\n", l, r, mid, ql, qr);
	rep(v, l, mid)
		for(int i = head[v]; i; i = nxt[i]) Add(pos[i].x, pos[i].y, 1);
	int cnt = 0, lim;
	rep(i, ql, qr) {
//		printf("(%d, %d)(%d, %d)\n", qs[i].x1, qs[i].y1, qs[i].x2, qs[i].y2);
		int tmp = Query(qs[i].x1, qs[i].y1, qs[i].x2, qs[i].y2);
		if(qs[i].k > tmp) qs[i].k -= tmp, qs[i].type = 1;
		else qs[i].type = 0, tqs[++cnt] = qs[i];
	}
	rep(v, l, mid)
		for(int i = head[v]; i; i = nxt[i]) Add(pos[i].x, pos[i].y, -1);
	lim = cnt;
	rep(i, ql, qr) if(qs[i].type) tqs[++cnt] = qs[i];
	rep(i, ql, qr) qs[i] = tqs[i-ql+1];
	solve(l, mid, ql, ql + lim - 1); solve(mid + 1, r, ql + lim, qr);
	return ;
}

int main() {
	n = read(); q = read();
	int cntn = 0;
	rep(i, 1, n) rep(j, 1, n) num[++cntn] = A[i][j] = read();
	rep(i, 1, q) {
		int x1 = read(), y1 = read(), x2 = read(), y2 = read(), K = read();
		qs[i] = Que(x1, y1, x2, y2, K, i);
	}
	
	sort(num + 1, num + cntn + 1);
	rep(i, 1, n) rep(j, 1, n)
		A[i][j] = lower_bound(num + 1, num + cntn + 1, A[i][j]) - num,
		pos[++ToT] = mp(i, j), nxt[ToT] = head[A[i][j]], head[A[i][j]] = ToT;
	solve(1, cntn, 1, q);
	
	rep(i, 1, q) printf("%d\n", Ans[i]);
	
	return 0;
}
posted @ 2017-10-24 22:20  xjr01  阅读(208)  评论(0编辑  收藏  举报