bzoj2738 矩阵乘法

2738: 矩阵乘法

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

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

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\n",ans[i]);

    return 0;
}

 

 

posted @ 2018-03-16 21:29  zbtrs  阅读(236)  评论(0编辑  收藏  举报