2738: 矩阵乘法
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1828 Solved: 792
[Submit][Status][Discuss]
Description
给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。
Input
第一行两个数N,Q,表示矩阵大小和询问组数;
接下来N行N列一共N*N个数,表示这个矩阵;
再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。
接下来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
2 1
3 4
1 2 1 2 1
1 1 2 2 3
Sample Output
1
3
3
HINT
100%的数据:N<=500,Q<=60000。
分析:很容易看出是整体二分吧,只不过从一维变成了二维.
我用bzoj3110的方法去做,开了一个二维线段树,结果T了,原因是每次都要枚举行,在每一行上进行操作,在枚举上非常浪费时间. 怎么办呢?树状数组呗......只涉及到单点修改,区间查询. 用二维树状数组维护即可.
结果还是T了......为什么呢,因为我直接用了long long.事实上int就可以了. 这也启发了我如果确定不可能爆int的情况下不要轻易用long long!可能爆并且时间很宽松的话,全部上long long可以保证不出错. 否则就只能把可能会爆int的变成long long咯.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 510; int n,q,a[maxn][maxn],sum[maxn][maxn << 2],tag[maxn][maxn << 2]; int L[maxn][maxn << 2],R[maxn][maxn << 2],id[60010],cnt,tot,ans[60010]; int t1[60010],t2[60010],cover[maxn][maxn << 2],c[maxn][maxn]; struct node { int l1,l2,r1,r2,k; }e[60010]; struct node2 { int x,y,v; }e2[maxn * maxn]; bool cmp(node2 a,node2 b) { return a.v < b.v; } void add(int x,int y,int v) { for (int i = x; i <= n; i += i & (-i)) for (int j = y; j <= n; j += j & (-j)) c[i][j] += v; } int Query(int x,int y) { int res = 0; for (int i = x; i; i -= i & (-i)) for (int j = y; j; j -= j & (-j)) res += c[i][j]; return res; } int query(int l1,int r1,int l2,int r2) { return Query(l2,r2) - Query(l1 - 1,r2) - Query(l2,r1 - 1) + Query(l1 - 1,r1 - 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++) { int temp = id[i]; ans[temp] = e2[l].v; } return; } int p1 = 0,p2 = 0; int mid = (l + r) >> 1; for (int i = l; i <= mid; i++) add(e2[i].x,e2[i].y,1); for (int i = L; i <= R; i++) { int res = 0,temp = id[i]; res += query(e[temp].l1,e[temp].r1,e[temp].l2,e[temp].r2); if (res < e[temp].k) { e[temp].k -= res; t2[++p2] = temp; } else t1[++p1] = temp; } for (int i = l; i <= mid; i++) add(e2[i].x,e2[i].y,-1); for (int i = 1; i <= p1; i++) id[i + L - 1] = t1[i]; for (int i = 1; i <= p2; i++) id[i + L + p1 - 1] = t2[i]; solve(L,L + p1 - 1,l,mid); solve(L + p1,R,mid + 1,r); } int main() { freopen("test.txt","r",stdin); scanf("%d%d",&n,&q); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { scanf("%d",&a[i][j]); e2[++tot].v = a[i][j]; e2[tot].x = i; e2[tot].y = j; } sort(e2 + 1,e2 + 1 + tot,cmp); for (int i = 1; i <= q; i++) { scanf("%d%d%d%d%d",&e[i].l1,&e[i].r1,&e[i].l2,&e[i].r2,&e[i].k); id[i] = i; } solve(1,q,1,n * n); for (int i = 1; i <= q; i++) printf("%d ",ans[i]); return 0; }