[SDOI2010] 粟粟的书架

本文介绍了一种针对特定数据规模的二维矩阵查询优化方法,通过预处理实现高效查询,适用于不同大小的数据集。对于较小的数据集,采用数组维护矩形前缀和的方法;而对于较大的线性数据集,则使用主席树进行维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

给出 N * M 的 表格 每个位置有一个权值 K
给出 Q 个查询
每次查询限定一个矩形区域 问至少在本矩形区域内取出多少个元素才可以>=Qi

题目分析:

YY了半天没想出来
看了下数据范围就懂了!
对于50%的数据,满足R, C≤200,M≤200,000;
另有50%的数据,满足R=1,C≤500,000,M≤20,000;
很明显是要我们分情况写算法
前50%数据 由于范围较小 我们可以直接 开数组维护矩形前缀和
num[i][j][k]表示以i,j,为右上角的且元素>=k矩形前缀和
f[i][j][k]表示以i,j为右上角的且元素>=k的矩形元素个数
对于每个查询 我们二分一个 高度
那么答案即为 元素个数-(高度和-查询)/答案高度
预处理 O(M*N*1000) 查询 O(Q*LogH)
对于后50%数据
从矩形变成了线段,那么我们考虑也像上面一样维护一样的东西,只是变成了主席树维护
查询答案也为二分查询
BZOJ卡一下内存

题目链接:

;uogu 2468
BZOJ 1926

Ac 代码:

#include <cstdio>
#include <iostream>
#define il inline
const int maxm1=5000010;
const int N=1000;
int sum[maxm1*2],v[maxm1*2],ls[maxm1*2],rs[maxm1*2],root[maxm1],sz;
int n,m,q;
int f[202][210][N+10],num[202][202][N+10],mx[210][210];
void insert(int &now,int pre,int l,int r,int k)
{
  now=++sz;
  ls[now]=ls[pre],rs[now]=rs[pre],sum[now]=sum[pre]+k,v[now]=v[pre]+1;
  if(l==r) return;
  int mid=(l+r)>>1;
  if(k<=mid) insert(ls[now],ls[pre],l,mid,k);
  else insert(rs[now],rs[pre],mid+1,r,k);
}
il int getans1(int x,int y,int k)
{
  int l=1,r=1000,ans=0;
  x=root[x-1],y=root[y];
  if(sum[y]-sum[x]<k) return -1;
  while(l<r)
  {
    int mid=(l+r)>>1;
    int sumx=sum[rs[y]]-sum[rs[x]];
    if(sumx<k)
    {
      ans+=(v[rs[y]]-v[rs[x]]);
      k-=sumx,r=mid;
      x=ls[x],y=ls[y];
    }
    else
    {
      l=mid+1;
      x=rs[x],y=rs[y];
    }
  }
  return ans+(k+l-1)/l;
}
il void work1()
{
   for(int i=1,x;i<=m;i++)
    scanf("%d",&x),insert(root[i],root[i-1],1,N,x);
   for(int i=1;i<=q;i++)
   {
    int x1,x2,y1,y2,k,d;
    scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k);
    if(~(d=getans1(y1,y2,k))) printf("%d\n",d);
    else printf("Poor QLW\n");
   }
}
il int getans2(int x1,int y1,int x2,int y2,int k)
{
  x1--,y1--;
  int l=0,r=N+10;
  while(l+1<r)
  {
    int mid=(l+r)>>1;
      if(num[x2][y2][mid]-num[x2][y1][mid]-num[x1][y2][mid]+num[x1][y1][mid]>=k) l=mid;
      else r=mid;
  }
  if(!l) return -1;
  int ans1=num[x2][y2][l]-num[x2][y1][l]-num[x1][y2][l]+num[x1][y1][l]-k;
  int ans2=f[x2][y2][l]-f[x2][y1][l]-f[x1][y2][l]+f[x1][y1][l];
  return ans2-ans1/l;
}
il void work2()
{
  for(int i=1;i<=n;i++)
   for(int j=1;j<=m;j++)
   scanf("%d",&mx[i][j]);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    for(int k=1;k<=N;k++)
    {
        f[i][j][k]=f[i][j-1][k]+f[i-1][j][k]-f[i-1][j-1][k];
        num[i][j][k]=num[i][j-1][k]+num[i-1][j][k]-num[i-1][j-1][k];
        if (mx[i][j]>=k) f[i][j][k]++,num[i][j][k]+=mx[i][j];
    }
    for(int i=1;i<=q;i++)
    {
      int x1,x2,y1,y2,k,d;
      scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k);
      if(~(d=getans2(x1,y1,x2,y2,k))) printf("%d\n",d);
        else printf("Poor QLW\n");
    }
}
int main()
{
   scanf("%d%d%d",&n,&m,&q);
   if(n==1) work1();
   else work2();
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值