zoto HDU6959

题目链接: HDU6959

题目大意:给定\(n\)个位于第一象限的点,\(q\)次询问,每次给出一个矩形范围,求此范围内有多少纵坐标不同的点。

​ 我们可以尝试先考虑一个子问题:在\(y\)轴上某位置加\(1\)并查询前缀中\(1\)的个数,显然我们可以用各种数据结构:线段树,树状数组,分块来解决。解决这个问题后,假如我们矩形框选范围为\((x1,y1) (x2,y2)\)(左下至右上),那么我们只要将 \(\left [ x1,x2 \right ]\) 范围内的点添加,并查询前缀\(y2\)\(y1-1\)的差即可。

​ 下面的问题便是,我们该如何快速的维护这\(q\)个矩形所覆盖的\(x\)轴上范围。最初在考场上想的线段树套树状数组,但发现我不会写动态开点...而静态数组一定会\(MLE\)。所以这题咕咕咕了,最后发现可以用莫队来维护。

​ 莫队实际上是一种较为优雅的暴力,常规的莫队适用于离线处理多个区间问题,以\(O(1)\)速度从区间\([l,r]\)转移至 \(\left [l,r \right ]\)\([l-1,r]\)\([l,r+1]\)\([l,r-1]\)。复杂度为\(O(n\sqrt{n})\)\(std\)中使用了莫队套分块,但经过实际测试,莫队套树状数组也可以,但每次操作复杂度变为了\(O(\log_{}{n} )\)级别。

​ 还有一些小细节问题,众所周知,树状数组维护的数据下标只能从\(1\)开始,而给定数组不乏位于\(x\)轴上,所以要将每个涉及\(y\)坐标的数据加\(1\)。此外,我们维护的是区间上不同\(y\)坐标的点,所以要考虑每次转移时转移点的\(y\)坐标的唯一性以及其是否会对答案有贡献,最后就是数据的初始化。

完整代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<string.h>
#include<cmath>
#include<deque>
#include<vector>
#define Lint  long long
using namespace std;
const int N=1e5+10;
struct node{
    int xl,yl,xr,yr,id,pos;
};
node a[N];
int T,n,m,Ans[N],f[N],num[N];
int c[N];
int lowbit(int x){
    return x&-x;
}
int Sum(int x){//树状数组的求前缀和
    int sum=0;
    while(x>=1){
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}
void add(int x,int y){
    while(x<=100002){
        c[x]+=y;
        x+=lowbit(x);
    }
}
bool cmp(node x,node y){
    if(x.pos==y.pos){
        if(x.pos&1){
            return x.xr<y.xr;
        }
        return x.xr>y.xr;
    }
    return x.pos<y.pos;
}
void solve(){
    memset(num,0,sizeof(num));
    memset(c,0,sizeof(c));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&f[i]);
        f[i]+=2;
    }
    int Size=(int)sqrt(n);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d%d",&a[i].xl,&a[i].yl,&a[i].xr,&a[i].yr);
        a[i].yl+=2;
        a[i].yr+=2;
        a[i].id=i;
        a[i].pos=(a[i].xl-1)/Size+1;
    }
    sort(a+1,a+1+m,cmp);
    int XL=a[1].xl,XR=a[1].xr;
    for(int i=a[1].xl;i<=a[1].xr;++i){
        num[f[i]]++;
        if(num[f[i]]==1){
            add(f[i],1);
        }
    }
    Ans[a[1].id]=Sum(a[1].yr)-Sum(a[1].yl-1);
    for(int i=2;i<=m;++i){
        while(XL>a[i].xl){
            XL--;
            num[f[XL]]++;
            if(num[f[XL]]==1){//此次操作改变了此y轴处的唯一性
                add(f[XL],1);//树状数组的单点修改
            }
        }
        while(XR<a[i].xr){
            XR++;
            num[f[XR]]++;
            if(num[f[XR]]==1){     
                add(f[XR],1);
            }
        }
        while(XL<a[i].xl){
            num[f[XL]]--;
            if(!num[f[XL]]){
                add(f[XL],-1);
            }
            XL++;
        }
        while(XR>a[i].xr){
            num[f[XR]]--;
            if(!num[f[XR]]){
                add(f[XR],-1);
            }
            XR--;
        }
        Ans[a[i].id]=Sum(a[i].yr)-Sum(a[i].yl-1);
    }
    for(int i=1;i<=m;++i){
        printf("%d\n",Ans[i]);
    }
}
int main(){
    //  freopen("1.in","r",stdin);
    //  freopen("c.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        solve();
    }
    return 0;
}
posted @ 2021-07-29 10:37  wzyyy  阅读(55)  评论(0)    收藏  举报