1 条题解

  • 0
    @ 2022-2-9 21:16:37

    本题有一个比较显然的莫队做法,时间复杂度 O(nnlogn)O(n\sqrt{n} \log n),可以过,但这里不讲。

    先不考虑去重的条件,如果是第 2,32,3 个测试点,那么发现答案可以前缀和,直接对 l1l-1rr 离线一下,就可以直接统计答案了。

    考虑对 rr 离线,统计所有右端点位 rr 的答案。

    那么如果有一堆 xx,我显然只取最后面的。

    那么我只需要支持动态加数删数,查询前缀的一个字典树上和。

    这个可以直接用树状数组套字典树,时间复杂度 O(nlog2n)O(n\log^2 n)


    代码
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    int n,a[N],s[N*330],tr[N*330][2],Q,ans[N],las[N],tot,rt[N];
    struct que{int l,a,b,id;};
    vector<que> vec[N];
    void add(int now,int x,int z){
        for(int i=20;i>=0;i--){
            s[now]+=z;
            int bit=(x>>i)&1;
            if(!tr[now][bit])tr[now][bit]=++tot;
            now=tr[now][bit];
        }
        s[now]+=z;
    }
    int ask(int now,int x,int k){
        int ans=0;
        for(int i=20;i>=0;i--){
            if(!now)return ans;
            int bitx=(x>>i)&1,bitk=(k>>i)&1;
            if(bitk)ans+=s[tr[now][bitx]],now=tr[now][bitx^1];
            else now=tr[now][bitx];
        }
        return ans+s[now];
    }
    void tradd(int x,int z,int id){for(;x<=n;x+=x&-x)add(rt[x],id,z);}
    int trask(int x,int a,int b){int ans=0;for(;x;x-=x&-x)ans+=ask(rt[x],a,b);return ans;}
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        scanf("%d",&Q);
        for(int i=1;i<=n;i++)rt[i]=i;
        tot=n;
        for(int i=1,l,r,A,B;i<=Q;i++)scanf("%d%d%d%d",&l,&r,&A,&B),vec[r].push_back((que){l,A,B,i});
        for(int i=1;i<=n;i++){
            if(las[a[i]])tradd(las[a[i]],-1,a[i]);
            tradd(i,1,a[i]);
            las[a[i]]=i;
            for(auto j:vec[i])ans[j.id]=trask(i,j.a,j.b)-trask(j.l-1,j.a,j.b);
        }
        for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
    }
    
    • 1

    信息

    ID
    72
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    6
    已通过
    1
    上传者