zoto(2021“MINIEYE杯”中国大学生算法设计超级联赛(1))

这道题巧妙地运用到了分块和莫队两种算法的结合。对于1-n的每一个X都有一个w[i]来代表其高度,然后其实这道题就是在之前的普通莫队的基础上在你统计出来每一个只有多少个之后,再去数出在对定范围内的值有几种。

对于每一个x轴上的移动,我们可以按莫队去计算,然后对于每一个查询,我们的答案可以用分块的思想去在sqrt(n)的时间内查询出来。

其实这道题难得地方就在于思维转化,对于一个二维的查询,我们要把他降为一维,一个y其实就相当于是这个点的权重,想到这里这道题就迎刃而解了。

时间复杂度等于莫队:Q*sqrt(n)+n*sqrt(n) +分块查询 Q*sqrt(maxn)

然后对于要想实现分块查询,我们就要多设一个sum数组,去统计一个块内共有几种不同的y值

这里的cnt数组就相当于a数组,sum数组就相当于Lazy数组。

void sub(int x){

    if(--cnt[x] == 0) sum[pos[x]]--;
}
void add(int x){

    if(cnt[x]++ == 0) sum[pos[x]]++;
}

查询时是用分块思想

int query(int l,int r){

    int res=0;
    for(int i = l ; i <= min(r,BK2(l)) ; i++){
        if(cnt[i] >= 1) res++;
    }
    if(pos[l] != pos[r]){
        for(int i = BK1(r) ; i <= r ; i++){
            if(cnt[i] >= 1) res++;
        }
    }
    for(int i = pos[l]+1 ; i <= pos[r]-1 ; i++){
        res+=sum[i];
    }return res;

}

然后总的代码是:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define BK1(x) ((pos[x]-1)*bk+1)
#define BK2(x) (pos[x]*bk)
using namespace std;
const int maxn = 1e5+10;
int n,k,w[maxn],bk,pos[maxn],sum[maxn],cnt[maxn],ans[maxn];
struct node{

    int xl,yl,xr,yr,id;
}a[maxn];
void sub(int x){

    if(--cnt[x] == 0) sum[pos[x]]--;
}
void add(int x){

    if(cnt[x]++ == 0) sum[pos[x]]++;
}
int query(int l,int r){

    int res=0;
    for(int i = l ; i <= min(r,BK2(l)) ; i++){
        if(cnt[i] >= 1) res++;
    }
    if(pos[l] != pos[r]){
        for(int i = BK1(r) ; i <= r ; i++){
            if(cnt[i] >= 1) res++;
        }
    }
    for(int i = pos[l]+1 ; i <= pos[r]-1 ; i++){
        res+=sum[i];
    }return res;

}
void solve(){

    memset(cnt,0,sizeof cnt);
    memset(sum,0,sizeof sum);
    bk=sqrt(n);
    for(int i = 1; i <= n ; i++){
        scanf("%d",&w[i]);
        pos[i]=(i-1)/bk+1;
    }

    for(int i = 1; i <= k ; i++){
        scanf("%d %d %d %d",&a[i].xl,&a[i].yl,&a[i].xr,&a[i].yr);
        a[i].id=i;
    }

    sort(a+1,a+k+1,[](node x,node y){
          if(pos[x.xl]!=pos[y.xl]) return pos[x.xl] < pos[y.xl];
          if(pos[x.xl]&1)  return x.xr < y.xr;
          return x.xr > y.xr;
    });

    for(int i = 1,R=0,L=1; i <= k ; i++){
        int l=a[i].xl,r=a[i].xr;
        while(L<l) sub(w[L++]);
        while(L>l) add(w[--L]);
        while(R>r) sub(w[R--]);
        while(R<r) add(w[++R]);
        ans[a[i].id]=query(a[i].yl,a[i].yr);
    }

    for(int i = 1; i <= k ; i++){
        printf("%d\n",ans[i]);
    }
}
int main()
{
    int t;scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&k);
        solve();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值