BZOJ 2738 矩阵乘法(分块)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2738

题意:给出一个矩阵。每次询问子矩阵第K小值。

思路:首先将矩阵的数字排序。设置size,每次将size个数字插入。插入时,我们用h[i][j]记录该位置的数字是否已经插入;用sum[i][j]表示子矩阵(1,1)到(i,j)已经插入的数字个数总和。每次插入后,暴力扫一次询问,若查询子矩阵的数字个数大于等于K则答案就在此次插入的数字中;然后将该询问从链表中删除。




struct node
{
    int k,x,y;
    
    int operator<(const node &a) const
    {
        return k<a.k;
    }
};


node a[N*N];


struct Node
{
    int x1,y1,x2,y2,ans,K;
    Node *next,*pre;
    
    void get()
    {
        RD(x1,y1); RD(x2,y2); RD(K);
    }
};


Node b[N*N];
int h[N][N],sum[N][N];
int n,m,size;


int get(Node *p)
{
    int x1=p->x1,y1=p->y1;
    int x2=p->x2,y2=p->y2;
    return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}


void deal()
{
    size=2.5*n*n/sqrt(1.0*m)+1;
    int i,j,k,L,R;
    Node *p,*end=&b[m+1];
    for(L=1;L<=n*n;L+=size)
    {
        R=min(n*n,L+size-1);
        for(i=L;i<=R;i++) h[a[i].x][a[i].y]=1;
        FOR1(i,n) FOR1(j,n) 
        {
            sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+h[i][j];
        }
        for(p=b[0].next;p!=end;p=p->next)
        {
            k=get(p);
            if(k<p->K) continue;
            for(i=R;i>=L;i--)
            {
                if(a[i].x>=p->x1&&a[i].x<=p->x2&&a[i].y>=p->y1&&a[i].y<=p->y2)
                {
                    if(k==p->K)
                    {
                        p->ans=a[i].k;
                        break;
                    }
                    k--;
                }
            }
            p->pre->next=p->next;
            p->next->pre=p->pre;
        }
    }
}


int main()
{
    RD(n,m);
    int i,j,k=0;
    FOR1(i,n) FOR1(j,n) 
    {
        RD(a[++k].k);
        a[k].x=i;
        a[k].y=j;
    }
    sort(a+1,a+k+1);
    FOR1(i,m) 
    {
        b[i].get();
        b[i].next=&b[i+1];
        b[i+1].pre=&b[i];
    }
    b[0].next=&b[1]; 
    b[1].pre=&b[0];
    deal();
    FOR1(i,m) PR(b[i].ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>