洛谷 P1527 [国家集训队]矩阵乘法 整体二分

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/81915669

题目描述
给你一个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。

分析:
我们可以对每个询问二分答案,但是这样每次询问都要把前缀和跑出来,显然非常慢。
注意到当我们把前缀和跑出来时,可以对所有询问进行判断,然后把小于等于 m i d 的放在左边,大于 m i d 的放在右边,递归处理,这个很像 c d q
然后我就真的对值域二分……
其实我们可以先对矩阵内的位置排序,二分这个数组显然更优秀。每次进行操作后还原二维树状数组即可,到小区间后插入和删除操作会边少,区间 O ( l e n ) 也能承受,这个也和 c d q 很像。
二维树状数组和一维差不多,一层循环变二层。复杂度是 O ( N 2 l o g 3 N 2 ) ,但是还是跑得很快的。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>

const int maxn=507;
const int maxq=6e4+7;
const int maxd=1e9+7;

using namespace std;

int t[maxn][maxn];
int n,test,cnt,x;

struct rec{
    int x,y,k;
}a[maxn*maxn];

struct node{
    int a,b,c,d,k,num,ans;
}q[maxq],L[maxq],R[maxq];

bool cmp(node x,node y)
{
    return x.num<y.num;
}

bool cmp1(rec x,rec y)
{
    return x.k<y.k;
}

void ins(int x,int y,int d)
{
    for (int i=x;i<=n;i+=i&(-i))
    {
        for (int j=y;j<=n;j+=j&(-j))
        {
            t[i][j]+=d;
        }
    }
}

int getsum(int x,int y)
{
    int sum=0;
    for (int i=x;i>0;i-=i&(-i))
    {
        for (int j=y;j>0;j-=j&(-j))
        {
            sum+=t[i][j];
        }
    }
    return sum;
}

int count(int a,int b,int c,int d)
{
    return getsum(c,d)-getsum(a-1,d)-getsum(c,b-1)+getsum(a-1,b-1);
}

void solve(int l,int r,int ql,int qr)
{
    if (ql>qr) return;
    if (l==r)
    {
        for (int i=ql;i<=qr;i++) q[i].ans=a[l].k;
        return;
    }
    int mid=(l+r)/2;    
    for (int i=l;i<=mid;i++) ins(a[i].x,a[i].y,1);
    int cnt1=0,cnt2=0;
    for (int i=ql;i<=qr;i++)
    {
        int d=count(q[i].a,q[i].b,q[i].c,q[i].d);
        if (q[i].k<=d) L[++cnt1]=q[i];
                  else q[i].k-=d,R[++cnt2]=q[i];
    }
    for (int i=l;i<=mid;i++) ins(a[i].x,a[i].y,-1);
    for (int i=1;i<=cnt1;i++) q[ql+i-1]=L[i];
    for (int i=1;i<=cnt2;i++) q[ql+cnt1-1+i]=R[i];
    solve(l,mid,ql,ql+cnt1-1);
    solve(mid+1,r,ql+cnt1,qr);
}

int main()
{
    scanf("%d%d",&n,&test);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++)
        {
            scanf("%d",&x);
            a[++cnt]=(rec){i,j,x};
        }
    }
    for (int i=1;i<=test;i++)
    {
        scanf("%d%d%d%d%d",&q[i].a,&q[i].b,&q[i].c,&q[i].d,&q[i].k);
        q[i].num=i;
    }
    sort(a+1,a+cnt+1,cmp1);
    solve(1,cnt,1,test);
    sort(q+1,q+test+1,cmp);
    for (int i=1;i<=test;i++) printf("%d\n",q[i].ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81915669
今日推荐