zoukankan      html  css  js  c++  java
  • 【bzoj3236】[Ahoi2013]作业【树套树 线段树套主席树】

    题目链接
    蒟蒻不会莫队,只好用树套树。
    看完这道题,是不是想起了【bzoj2120】数颜色
    如果只是查询l~r区间内的不同数字的个数,就在每个位置记录一个pre值,代表前一个与它相同的位置。这样问题就转化为了l~r之间有多少个位置的pre值 < l,直接上主席树即可。
    再带上一个权值要在a~b之间,怎么办?先想一个很暴力的做法。在上述做法的基础上再在外面套一棵线段树,也就是一棵线段树每个节点上套一棵主席树。不用说,肯定爆空间。
    怎么办呢?蒟蒻博主苦恼了很久。感谢wyc大神提供思路!其实很简单,对于每次查询,在外层的线段树加一个标记。最后把线段树遍历一遍,建当前节点对应区间的主席树,然后一次性把所有在这个节点上的查询给搞定。这样空间就够用了。
    时间复杂度:线段树有logn层,每一层有n个位置,每个位置要在权值树上用logn的时间插入一次,每个询问被拆分为logn个,然后拆分后的询问每个要用logn的时间查询,因此时间复杂度是O((n+m) log^2 n)。理论上比莫队优,但实际上。。。不知道。
    然后是很短的代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N=100005,M=1000005;
    int n,m,l,r,a,b,num[N],last[N],pre[N],Hash[N],ans[M][2];
    int cnt,root[N],sum[N*20],lc[N*20],rc[N*20];
    struct Query{
        int a,b,l,id;
    };
    struct data{
        int a,v;
        bool operator < (const data &b) const{
            return a<b.a;
        }
    }d[N];
    vector<Query> q[N*4];
    void addquery(int o,int l,int r,int L,int R,Query qry){
        if(L<=l&&R>=r){
            q[o].push_back(qry);
            return;
        }
        int mid=(l+r)/2;
        if(L<=mid){
            addquery(o*2,l,mid,L,R,qry);
        }
        if(R>mid){
            addquery(o*2+1,mid+1,r,L,R,qry);
        }
    }
    void build(int y,int &x,int l,int r,int k){
        x=++cnt;
        sum[x]=sum[y]+1;
        lc[x]=lc[y];
        rc[x]=rc[y];
        if(l==r){
            return;
        }
        int mid=(l+r)/2;
        if(k<=mid){
            build(lc[y],lc[x],l,mid,k);
        }else{
            build(rc[y],rc[x],mid+1,r,k);
        }
    }
    int query(int y,int x,int l,int r,int k){
        if(!x||l==r){
            return 0;
        }
        int mid=(l+r)/2;
        if(k<=mid){
            return query(lc[y],lc[x],l,mid,k);
        }else{
            return sum[lc[x]]-sum[lc[y]]+query(rc[y],rc[x],mid+1,r,k);
        }
    }
    void solve(int o,int l,int r){
        if(q[o].size()){
            Hash[0]=0;
            for(int i=l;i<=r;i++){
                Hash[++Hash[0]]=num[i];
            }
            sort(Hash+1,Hash+Hash[0]+1);
            int s=0;
            for(int i=l;i<=r;i++){
                d[++s].a=lower_bound(Hash+1,Hash+Hash[0]+1,num[i])-Hash;
                d[s].v=pre[i];
            }
            sort(d+1,d+s+1);
            for(int i=1;i<=s;i++){
                build(root[i-1],root[i],0,n,d[i].v);
            }
            int a,b;
            for(int i=0;i<q[o].size();i++){
                a=lower_bound(Hash+1,Hash+Hash[0]+1,q[o][i].a)-Hash;
                b=upper_bound(Hash+1,Hash+Hash[0]+1,q[o][i].b)-Hash-1;
                ans[q[o][i].id][0]+=sum[root[b]]-sum[root[a-1]];
                ans[q[o][i].id][1]+=query(root[a-1],root[b],0,n,q[o][i].l);
            }
            memset(root,0,sizeof(int)*(Hash[0]+1));
            memset(sum,0,sizeof(int)*(cnt+1));
            memset(lc,0,sizeof(int)*(cnt+1));
            memset(rc,0,sizeof(int)*(cnt+1));
            cnt=0;
        }
        if(l==r){
            return;
        }
        int mid=(l+r)/2;
        solve(o*2,l,mid);
        solve(o*2+1,mid+1,r);
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&num[i]);
            pre[i]=last[num[i]];
            last[num[i]]=i;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d%d",&l,&r,&a,&b);
            addquery(1,1,n,l,r,(Query){a,b,l,i});
        }
        solve(1,1,n);
        for(int i=1;i<=m;i++){
            printf("%d %d
    ",ans[i][0],ans[i][1]);
        }
        return 0;
    }
  • 相关阅读:
    2021年1月4号
    2021年1月3号
    2021年1月2日
    2021年1月1日
    Jenkins定时构建与轮询SCM
    2017-08-22校验
    2017-08-21xlVBASplitSheetsSameTime
    20170821xlVBA跨表公式套用
    20170821xlVBA隐藏空行
    20170814xlVBA限定日期按客户分类汇总
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476903.html
Copyright © 2011-2022 走看看