[2018雅礼集训1-18]风吕 分块

B=nm ,先把权值离散化,然后排序,把每 B 个权值分成一块,维护一个二维前缀和。询问时整块的O(1),单块的 O(B) 暴力,总复杂度 O(qB)
当然还有各种数据结构做法,比如说可持久化{线段树套线段树}。。。外层线段树维护X轴,内层线段树维护Y轴,然后权值离散排序,每次添加一个权值也就只会改变一个地方,可持久化的那条链要从外层线段树一直延伸到内层线段树,才能每次保证每次只新建 log2 个点,时间复杂度 O(nmlog2n) ,空间复杂度 O(nmlog2n) 。因为两个线段树大小都只有 300 ,所以相比其他数据结构做法常数小些。
代码(分块):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define piii pair<int,pair<int,int> >
#define fs first
#define sc second
using namespace std;
const int B=300;
int n,m,num,q,type,lans,z[90010];
piii a[90010];
int read()
{
    int x=0;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;   
}
struct block
{
    int s[310][310],cl,cr,num;
    piii c[310];
    block(){cl=cr=num=0;memset(s,0,sizeof(s));}
    void ins(piii x) 
    {
        c[++num]=x;
        s[x.sc.fs][x.sc.sc]++;
    }
    void gets()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    }
    int qry1(int x1,int y1,int x2,int y2)
    {
        x1--;y1--;
        return s[x2][y2]-s[x1][y2]-s[x2][y1]+s[x1][y1];
    }
    int qry2(int x1,int y1,int x2,int y2,int s,int t)
    {
        int re=0;
        for(int i=1;i<=num;i++)
            re+=(c[i].fs>=s&&c[i].fs<=t&&c[i].sc.fs>=x1&&c[i].sc.fs<=x2&&c[i].sc.sc>=y1&&c[i].sc.sc<=y2);
        return re;  
    }
}blk[310];
int main()
{
    n=read();m=read();q=read();type=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[(i-1)*m+j]=make_pair(read(),make_pair(i,j)),z[(i-1)*m+j]=a[(i-1)*m+j].fs;
    sort(z+1,z+n*m+1);
    for(int i=1;i<=n*m;i++)
            a[i].fs=lower_bound(z+1,z+n*m+1,a[i].fs)-z;
    sort(a+1,a+n*m+1);
    for(int i=1,k=1;i<=n*m;i+=B,k++)
    {
        blk[k].cl=a[i].fs;blk[k].cr=a[min(i+B-1,n*m)].fs;
        for(int j=i;j<=min(i+B-1,n*m);j++)
            blk[k].ins(a[j]);
        blk[k].gets();
    }
    while(q--)
    {
        int x1=read(),y1=read(),x2=read(),y2=read(),s=read(),t=read();
        if(type) x1^=lans,x2^=lans,y1^=lans,y2^=lans,s^=lans,t^=lans;
        x1=(x1-1+n)%n+1;x2=(x2-1+n)%n+1;y1=(y1-1+m)%m+1;y2=(y2-1+m)%m+1;
        if(x1>x2) swap(x1,x2);
        if(y1>y2) swap(y1,y2);
        if(s>t) swap(s,t);
        s=lower_bound(z+1,z+n*m+1,s)-z;
        t=upper_bound(z+1,z+n*m+1,t)-z-1;
        lans=0;
        for(int i=1,k=1;i<=n*m;i+=B,k++)
            if(blk[k].cl>=s&&blk[k].cr<=t) lans+=blk[k].qry1(x1,y1,x2,y2);
            else if(blk[k].cl<=t&&blk[k].cr>=s) lans+=blk[k].qry2(x1,y1,x2,y2,s,t); 
        printf("%d\n",lans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值