luogu P1527 [国家集训队]矩阵乘法

https://www.luogu.com.cn/problem/P1527
整体二分板子题
二分一个mid,把<=mid的全部设为1
用二维树状数组维护前缀个数和
然后套板子即可
code:

#include<bits/stdc++.h>
#define N 606
#define lowbit(x) (x & -x)
using namespace std;
int ans[N * N], id[N * N], n, Q, A[N * N], B[N * N], cnt;
struct QQ {
	int x, y, xx, yy, k;
} q[N * N];
int t[N][N];
void add(int x, int y, int o) {
	for(int i = x; i <= n; i += lowbit(i))
		for(int j = y; j <= n; j += lowbit(j))
			t[i][j] += o;
}
int query(int x, int y) {
	int ret = 0;
	for(int i = x; i; i -= lowbit(i))
		for(int j = y; j; j -= lowbit(j))
			ret += t[i][j];
	return ret;
}
int calc(int x, int y, int xx, int yy) {
	return query(xx, yy) - query(x - 1, yy) - query(xx, y - 1) + query(x - 1, y - 1);
}

struct AA {
	int x, y, val;
} a[N * N];
int cmp(AA x, AA y) {
	return x.val < y.val;
}
void solve(int l, int r, int L, int R) { //printf("  %d %d %d %d\n", l, r, L, R);
	if(L > R) return ;
	if(l == r) {
		for(int i = L; i <= R; i ++) ans[id[i]] = a[l].val;
	//	printf("*%d*", l);
		return ;
	}
	int mid = (l + r) >> 1;
	for(int i = l; i <= mid; i ++) add(a[i].x, a[i].y, 1);
	int sz1 = 0, sz2 = 0;
	for(int i = L; i <= R; i ++) {
		int j = id[i], s = calc(q[j].x, q[j].y, q[j].xx, q[j].yy);
		if(s >= q[j].k) A[++ sz1] = j;
		else B[++ sz2] = j, q[j].k -= s;
	}
	for(int i = 1; i <= sz1; i ++) id[L + i - 1] = A[i];
	for(int i = 1; i <= sz2; i ++) id[L + sz1 - 1 + i] = B[i];
	for(int i = l; i <= mid; i ++) add(a[i].x, a[i].y, - 1);
	solve(l, mid, L, L + sz1 - 1),	solve(mid + 1, r, L + sz1, R);
}
int main() {
	scanf("%d%d", &n, &Q);
	for(int i = 1; i <= n; i ++) 
		for(int j = 1; j <= n; j ++) {
			scanf("%d", &a[++ cnt].val);
			a[cnt].x = i, a[cnt].y = j;
		}
	sort(a + 1, a  + 1 + cnt, cmp);
	for(int i = 1; i <= Q; 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 <= Q; i ++) id[i] = i;
	solve(1, cnt, 1, Q);
	for(int i = 1; i <= Q; i ++) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2021-07-12 08:58  lahlah  阅读(33)  评论(0编辑  收藏  举报