【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\n",ans[i][0],ans[i][1]);
    }
    return 0;
}
posted @ 2018-01-11 15:31  ez_2016gdgzoi471  阅读(122)  评论(0编辑  收藏  举报