Tsinsen A1333(矩阵乘法(梁 盾)-子矩阵第k小值)

A1333. 矩阵乘法(梁 盾)
时间限制: 2.0s   内存限制: 256.0MB  
总提交次数: 217   AC次数: 73   平均分: 50.69
将本题分享到:
       
   
试题来源
  2012中国国家集训队命题答辩
问题描述
  给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。
输入格式
  第一行两个数N,Q,表示矩阵大小和询问组数;
  接下来N行N列一共N*N个数,表示这个矩阵;
  再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。
输出格式
  对于每组询问输出第K小的数。
样例输入
2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3
样例输出
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
如果有,那么答案肯定在这次填入中。
那么先填入
--------------------------1---------------------1---1-1----------11-----
                                                                           ↑
1表示在这个子矩阵中的数,我们暴力往回数就行了。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define MAXN (500+10)
#define MAXQ (60000+10)
int n,m,a[MAXN][MAXN];
struct node
{
    int x,y,a;
    friend bool operator<(node a,node b){return a.a<b.a;}
}b[MAXN*MAXN];
struct comm
{
    int x1,y1,x2,y2,k;
    int next,pre,ans;
}ask[MAXQ];
int head=1;
void delt(int i)
{
    if (i==head) {if (head=ask[head].next) ask[head].pre=0;return;}
    if (ask[i].next) ask[ask[i].next].pre=ask[i].pre;
    if (ask[i].pre) ask[ask[i].pre].next=ask[i].next;
}
struct arr_tree
{
    int a[MAXN][MAXN];
    arr_tree(){memset(a,0,sizeof(a));}
    void add(int x,int y)
    {
        for(int i=x;i<=n;i+=i&(-i))
            for(int j=y;j<=n;j+=j&(-j)) /*cout<<i<<' '<<j<<' '<<endl,*/a[i][j]++;
    }
    int qur(int x,int y)
    {
        int ans=0;
        for(int i=x;i;i-=i&(-i))
            for(int j=y;j;j-=j&(-j)) /*cout<<i<<' '<<j<<endl,*/ans+=a[i][j];
        return ans;
    }
    int is_full(comm a)
    {
        int tmp;
        if ((tmp=qur(a.x2,a.y2)+qur(a.x1-1,a.y1-1)-qur(a.x1-1,a.y2)-qur(a.x2,a.y1-1))>=a.k)
        {
            return tmp;
        }
        return 0;
    }
}sum;
bool is_inside(comm a,node b)
{
    return a.x1<=b.x&&a.x2>=b.x&&a.y1<=b.y&&a.y2>=b.y;
}
int main()
{
//	freopen("tsinsenA1333.in","r",stdin);
    scanf("%d%d",&n,&m);
    For(i,n) For(j,n)
    {
        scanf("%d",&a[i][j]);
        int t=n*(i-1)+j;
        b[t].x=i,b[t].y=j,b[t].a=a[i][j];
    }
    sort(b+1,b+n*n+1);
    For(i,m) scanf("%d%d%d%d%d",&ask[i].x1,&ask[i].y1,&ask[i].x2,&ask[i].y2,&ask[i].k);
    For(i,m) ask[i].next=i+1,ask[i].pre=i-1;
    ask[1].pre=ask[m].next=0;
    int path=1,P=2.5*n*n/sqrt(m);
    For(i,n*n)
    {
        sum.add(b[i].x,b[i].y);
        if (path<P&&i<n*n) {path++;continue;}
        path=0;
        int delta;
        for(int now=head;now;now=ask[now].next)
            if (delta=sum.is_full(ask[now]))
            {
                delta-=ask[now].k;
                int j=i;
                while (delta!=-1) {delta-=is_inside(ask[now],b[j]);j--;}
                ask[now].ans=b[j+1].a;
                delt(now);
            }
    }
    For(i,m) cout<<ask[i].ans<<endl;
	return 0;
}






    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值