• Evanyou Blog 彩带


      题目传送门

    矩阵乘法

    题目描述

    给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

    输入输出格式

    输入格式:

     

    第一行两个数N,Q,表示矩阵大小和询问组数;

    接下来N行N列一共N*N个数,表示这个矩阵;

    再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

     

    输出格式:

     

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

     

    输入输出样例

    输入样例#1: 
    2 2
    2 1
    3 4
    1 2 1 2 1
    1 1 2 2 3
    输出样例#1: 
    1
    3

    说明

    矩阵中数字是10^9以内的非负整数;

    20%的数据:N<=100,Q<=1000;

    40%的数据:N<=300,Q<=10000;

    60%的数据:N<=400,Q<=30000;

    100%的数据:N<=500,Q<=60000。


      分析:

      是的,这道题虽然叫矩阵乘法,但是和矩阵乘法一点关系都没有。

      求矩阵$k$小就能想到用整体二分,不过因为是二维,所以需要用二维树状数组,然后写法需要漂亮一点,因为这题有点卡常。

      另外,有一点需要讲一下,平常我写树状数组都是这样的:

    inline void add(int pos,int x)
    {
        for(; pos<=n; pos+=lowbit(pos)) c[pos]+=x;  
    }

      但是在二维树状数组中就不能这么写,应该写成:

    inline void add(int x,int y,int v)
    {
        for(int i=x; i<=n; i+=lowbit(i))
        for(int j=y; j<=n; j+=lowbit(j)) c[i][j]+=v;
    }

      Code:

    //It is made by HolseLee on 6th Oct 2018
    //Luogu.org P1527
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N=4e5+7;
    int n,m,cnt,c[505][505],q1[N],q2[N],id[N],ans[N];
    struct Node {
        int x,y,v;
        Node() {}
        Node(const int _x,const int _y,const int _v):
            x(_x), y(_y), v(_v) {}
        bool operator < (const Node a) const {
            return v < a.v;
        }
    }a[N];
    struct Qus {
        int x1,y1,x2,y2,k;    
    }q[N];
        
    
    inline int read()
    {
        char ch=getchar(); int num =0; bool flag=false;
        while( ch<'0' || ch>'9' ) {
            if( ch=='-' ) flag=true; ch=getchar();
        }
        while( ch>='0' && ch<='9' ) {
            num=num*10+ch-'0'; ch=getchar();
        }
        return flag ? -num : num;
    }
    
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    
    inline void add(int x,int y,int v)
    {
        for(int i=x; i<=n; i+=lowbit(i)) 
        for(int j=y; j<=n; j+=lowbit(j)) c[i][j]+=v;
    }
    
    inline int quary(int x,int y)
    {
        int ret=0;
        for(int i=x; i; i-=lowbit(i)) 
        for(int j=y; j; j-=lowbit(j)) ret+=c[i][j];
        return ret;
    }
    
    inline int get(int x1,int y1,int x2,int y2) 
    {
        return quary(x2,y2)-quary(x1-1,y2)-quary(x2,y1-1)+quary(x1-1,y1-1);
    }
    
    void solve(int l,int r,int L,int R)
    {
        if( l>r || L>R ) return;
        if( l==r ) {
            for(int i=L; i<=R; ++i) ans[id[i]]=a[l].v;
            return;
        }
        int mid=(l+r)>>1, cnt1=0, cnt2=0;
        for(int i=l; i<=mid; ++i) add(a[i].x,a[i].y,1);
        for(int i=L; i<=R; ++i) {
            int tmp=get(q[id[i]].x1,q[id[i]].y1,q[id[i]].x2,q[id[i]].y2);
            if( tmp>=q[id[i]].k ) q1[++cnt1]=id[i];
            else q[id[i]].k-=tmp, q2[++cnt2]=id[i];
        }
        for(int i=l; i<=mid; ++i) add(a[i].x,a[i].y,-1);
        for(int i=1; i<=cnt1; ++i) id[L+i-1]=q1[i];
        for(int i=1; i<=cnt2; ++i) id[L+cnt1+i-1]=q2[i];
        solve(l,mid,L,L+cnt1-1); solve(mid+1,r,L+cnt1,R);
    }
    
    int main()
    {
        n=read(), m=read();
        for(int i=1; i<=n; ++i) 
        for(int j=1; j<=n; ++j){
            a[++cnt]=Node(i,j,read());
        }
        sort(a+1,a+cnt+1);
        for(int i=1; i<=m; ++i) {
            q[i].x1=read(), q[i].y1=read(), q[i].x2=read(), q[i].y2=read();
            q[i].k=read(); id[i]=i;
        }
        solve(1,cnt,1,m);
        for(int i=1; i<=m; ++i) printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    将指定目录的所有文件及文件夹copy到指定目录下,只copy 7天内创建的或是7天内修改过的
    修复置疑态状态的数据库 重建数据库日志
    修改MYSQL最大连接数的3种方法
    [转载]RRDTool 中文手册简易入门(二)
    [转载]RRDTool 快速学习思维导图
    sql server 2008 新功能创建压缩表和索引
    分析src=http://s.see9.us/s.js>亦或3b3.org注入攻击及解决方案探讨....
    创建login帐号及为其添加安全权限
    计数排序 Counting Sort
    基数排序 Radix sort
  • 原文地址:https://www.cnblogs.com/cytus/p/9747127.html
Copyright © 2020-2023  润新知