题目
题目描述
给你一个 n \times nn×n 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 kk 小数。
输入格式
第一行有两个整数,分别表示矩阵大小 nn 和询问组数 qq。
第 22 到第 (n + 1)(n+1) 行,每行 nn 个整数,表示这个矩阵。第 (i + 1)(i+1) 行的第 jj 个数表示矩阵第 ii 行第 jj 列的数 a_{i, j}a
i,j
。
接下来 qq 行,每行五个整数 x_1, y_1, x_2, y_2, kx
1
,y
1
,x
2
,y
2
,k,表示一组询问,要求找到以 (x_1, y_1)(x
1
,y
1
) 为左上角,(x_2, y_2)(x
2
,y
2
) 为右下角的子矩形中的第 kk 小数。
输出格式
对于每组询问,输出一行一个整数表示答案。
输入输出样例
输入 #1复制
2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3
输出 #1复制
1
3
说明/提示
数据规模与约定
对于 20%20% 的数据,保证 n \leq 100n≤100,q \leq 10^3q≤10
3
。
对于 40%40% 的数据,保证 n \leq 300n≤300,q \leq 10^4q≤10
4
。
对于 60%60% 的数据,保证 n \leq 400n≤400,q \leq 3 \times 10^4q≤3×10
4
。
对于 100%100% 的数据,保证 1 \leq n \leq 5001≤n≤500,1 \leq q \leq 6 \times 10^41≤q≤6×10
4
,0 \leq a_{i, j} \leq 10^90≤a
i,j
≤10
9
。
思路
这道题把序列查询区间第kk大搬到了矩阵上,但是仍然满足二分性质,所以我们还是可以整体二分
而我们沿用序列上的做法,把点和询问都离线下来,揉在一起整体二分
唯一不一样的地方就是需要二维树状数组
代码
#include<bits/stdc++.g>
const int N = 500;
const int M = 6e4;
const int N2 = 250000;
const int INF = 1e9;
using namespace std;
struct node
{
int x1,y1,x2,y2,k,id;
}q[M + N2 + 5],q1[M + N2 + 5],q2[M + N2 + 5];
int n,m,a[N + 5][N + 5],ans[M + 5],c[N + 5][N + 5],cnt;
int lowbit(int x)
{
return x & (-x);
}
void add(int x,int y,int s)
{
for (int i = x;i <= n;i += lowbit(i))
for (int j = y;j <= n;j += lowbit(j))
c[i][j] += s;
}
int query(int x,int y)
{
if (!x || !y)
return 0;
int ans = 0;
for (int i = x;i;i -= lowbit(i))
for (int j = y;j;j -= lowbit(j))
ans += c[i][j];
return ans;
}
int query_sum(int x1,int y1,int x2,int y2)
{
return query(x2,y2) - query(x1 - 1,y2) - query(x2,y1 - 1) + query(x1 - 1,y1 - 1);
}
void solve(int l,int r,int L,int R)
{
if (L > R)
return;
if (l == r)
{
for (int i = L;i <= R;i++)
if (q[i].id)
ans[q[i].id] = l;
return;
}
int mid = l + r >> 1,cnt1 = 0,cnt2 = 0;
for (int i = L;i <= R;i++)
if (q[i].id == 0)
{
if (q[i].k <= mid)
{
add(q[i].x1,q[i].y1,1);
q1[++cnt1] = q[i];
}
else
q2[++cnt2] = q[i];
}
else
{
int x = query_sum(q[i].x1,q[i].y1,q[i].x2,q[i].y2);
if (x >= q[i].k)
q1[++cnt1] = q[i];
else
{
q[i].k -= x;
q2[++cnt2] = q[i];
}
}
for (int i = 1;i <= cnt1;i++)
if (q1[i].id == 0)
add(q1[i].x1,q1[i].y1,-1);
for (int i = L;i <= L + cnt1 - 1;i++)
q[i] = q1[i - L + 1];
for (int i = L + cnt1;i <= R;i++)
q[i] = q2[i - L - cnt1 + 1];
solve(l,mid,L,L + cnt1 - 1);
solve(mid + 1,r,L + cnt1,R);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
{
scanf("%d",&a[i][j]);
q[++cnt] = (node){i,j,0,0,a[i][j],0};
}
for (int i = 1;i <= m;i++)
{
cnt++;
scanf("%d%d%d%d%d",&q[cnt].x1,&q[cnt].y1,&q[cnt].x2,&q[cnt].y2,&q[cnt].k);
q[cnt].id = i;
}
solve(0,INF,1,cnt);
for (int i = 1;i <= m;i++)
printf("%d\n",ans[i]);
return 0;
}