[BZOJ2738]矩阵乘法

2738: 矩阵乘法

Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1629  Solved: 710 [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小数。

Output

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

Sample Input

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

Sample Output

1
3

HINT

  矩阵中数字是109以内的非负整数;
  20%的数据:N<=100,Q<=1000;
  40%的数据:N<=300,Q<=10000;
  60%的数据:N<=400,Q<=30000;
  100%的数据:N<=500,Q<=60000。

 

整体二分,二维树状数组维护

国庆作业还没做要炸了。。。有时间具体写

Update:

把数字看成插入按照权值排序

然后二分答案,将权值小于等于二分值的全部插入

对于一个询问如果在区间内已经有足够多的数那么就可行

插入和删除可以用二维树状数组维护

#pragma GCC optimize ("O2") 
#include <cstdio>
#include <algorithm>
using namespace std;
char buf[10000000], *ptr = buf - 1;
inline int readint(){
    int n = 0;
    while(*++ptr < '0' || *ptr > '9');
    while(*ptr >= '0' && *ptr <= '9') n = (n << 1) + (n << 3) + (*ptr++ & 15);
    return n; 
}
const int maxn = 500 + 10, maxq = 60000 + 10;
int N, Q;
int Fen[maxn][maxn] = {0};
inline void Update(const int &x, const int &y, const int &val){
    for(int i = x; i <= N; i += i & -i)
        for(int j = y; j <= N; j += j & -j)
            Fen[i][j] += val;
}
inline int Query(const int &x, const int &y){
    int s = 0;
    for(int i = x; i; i -= i & -i)
        for(int j = y; j; j -= j & -j)
            s += Fen[i][j];
    return s;
}
struct Insert{
    int val, x, y;
    Insert(){}
    Insert(int _v, int _x, int _y): val(_v), x(_x), y(_y){}
    bool operator < (const Insert &a) const {
        return val < a.val;
    }
}a[maxn * maxn];
int num_cnt = 0;
struct Question{
    int x1, y1, x2, y2, k;
    void read(){
        x1 = readint();
        y1 = readint();
        x2 = readint();
        y2 = readint();
        k = readint();
    }
}q[maxq];
inline int Query(const Question &t){
    return Query(t.x2, t.y2) + Query(t.x1 - 1, t.y1 - 1) - Query(t.x1 - 1, t.y2) - Query(t.x2, t.y1 - 1);
}
int ans[maxq];
int T = 0;
int id[maxq], tmp[maxq];
bool mark[maxq];
void solve(int ql, int qr, int vl, int vr){
    if(ql > qr || vl == vr) return;
    int mid = vl + vr >> 1;
    while(T < N * N && a[T + 1].val <= mid){
        T++;
        Update(a[T].x, a[T].y, 1);
    }
    while(T && a[T].val > mid){
        Update(a[T].x, a[T].y, -1);
        T--;
    }
    int cnt = 0;
    for(int i = ql; i <= qr; i++)
        if(Query(q[id[i]]) >= q[id[i]].k){
            mark[i] = true;
            ans[id[i]] = mid;
            cnt++;
        }
        else mark[i] = false;
    int l1 = ql, l2 = ql + cnt;
    for(int i = ql; i <= qr; i++)
        if(mark[i]) tmp[l1++] = id[i];
        else tmp[l2++] = id[i];
    for(int i = ql; i <= qr; i++) id[i] = tmp[i];
    solve(ql, l1 - 1, vl, mid); solve(l1, qr, mid + 1, vr);
}
int main(){
    buf[fread(buf, sizeof(char), sizeof(buf), stdin)] = 0;
    N = readint();
    Q = readint();
    for(int i = 1; i <= N; i++)
        for(int j = 1; j <= N; j++)
            a[++num_cnt] = Insert(readint(), i, j);
    sort(a + 1, a + num_cnt + 1);
    for(int i = 1; i <= Q; i++) q[i].read();
    for(int i = 1; i <= Q; i++) id[i] = i;
    solve(1, Q, 0, a[num_cnt].val + 1);
    for(int i = 1; i <= Q; i++) printf("%d\n", ans[i]);
    return 0;
}

 

posted @ 2017-10-08 12:01  jzyy  阅读(166)  评论(0编辑  收藏  举报