【uva12345】dynamic len 树状数组套线段树

原题传送门
In python, we can use len(start(a[L:R])) to calculate the number of distinct values of elements a[L],
a[L + 1], … , a[R − 1].
Here are some interactive examples that may help you understand how it is done. Remember that
the indices of python lists start from 0.

>
a=[1,2,1,3,2,1,4]

print a[1:6]
[2, 1, 3, 2, 1]
print set(a[1:6])
set([1, 2, 3])
print
len(set(a[1:6]))
3
a[3]=2
print
len(set(a[1:6]))
2
print len(set(a[3:5]))
1
Your task is to simulate this process.
Input
There will be only one test case. The first line contains two integers n and m (1 ≤ n, m ≤ 50, 000).
The next line contains the original list.
All the integers are between 1 and 1,000,000 (inclusive). The next m lines contain the statements
that you need to execute.
A line formatted as ‘M x y’ (1 ≤ y ≤ 1, 000, 000) means “a[x] = y”, and a line formatted as ‘Q x
y’ means “print len(set(a[x : y]))”.
It is guaranteed that the statements will not cause “index out of range” error.
Output
Print the simulated result, one line for each query.
Sample Input
7 4
1 2 1 3 2 1 4
Q 1 6
M 3 2
Q 1 6
Q 3 5
Sample Output
3
2
1

题目大意:给一段序列,中途带单点修改,询问某区间中不同元素的种数。
额……有一个看起来挺像但是要弱得多的题(点这里过去,该题数值只有60种,可以直接状压,利用位运算可以做到O(nlogn)的复杂度),但此题颜色种类那么多,即使压位也是不可能的。如果还是考虑记录每种颜色出现的次数,使用线段树进行维护的话,我们很难找到合适的办法合并两个区间的答案。我们换一种统计答案的方式:找到每种数值第一次出现的位置。我们将值相同的点用双向链表串起来,记i的前驱和后继分别为pre(i)和suf(i),那么我们对于询问[l,r],我们只需要对于任意l<=i<=r,满足pre(i) < l 的i的个数。那么,我们便可以使用树套树来解决这个问题了:我们把pre(i)看作i的权值,外层是权值树状数组,树状数组的每个结点是一棵线段树,它维护了它所在树状数组的结点的维护的权值在不同区间出现的次数。回答询问直接转化为在树状数组在区间上的权值前缀和。
对于修改操作,我们分别对新旧前驱后继进行相应的修改即可。维护双向链表需要用到平衡树。可以使用set,但我讨厌stl……
一个细节:pre(i)对应的树状数组的位置,为了方便,使用pre(i)+1。
时空复杂度均为O(mlog^2n)。

#include<cstdio>
#include<iostream>
#define maxn 50000
#define maxv 1000000
#define mid (l+r>>1)
using namespace std;
int val[maxn+10],pre[maxn+10],suf[maxn+10],n;
struct splaytree{
    int fa[maxn+10],ch[maxn+10][2],root[maxv+10];
    int rc(int fa,int o){return ch[fa][1]==o;}
    void rot(int o){
        int f=fa[o],r=rc(f,o);
        fa[ch[fa[f]][rc(fa[f],f)]=o]=fa[f];
        fa[ch[f][r]=ch[o][r^1]]=f;
        fa[ch[o][r^1]=f]=o;
    }
    void splay(int o,int f){
        if(!f)root[val[o]]=o;
        while(fa[o]!=f){
            if(fa[fa[o]]!=f&&rc(fa[fa[o]],fa[o])==rc(fa[o],o))rot(fa[o]);
            rot(o);
        }
    }
    void insert(int &o,int f,int pos){
        if(!o){
            o=pos;
            ch[o][0]=ch[o][1]=0;
            fa[o]=f;
            splay(pos,0);
            int p=ch[pos][0],s=ch[pos][1];
            if(p){
                while(ch[p][1])p=ch[p][1];
                suf[p]=pos;
            }
            pre[pos]=p;
            if(s){
                while(ch[s][0])s=ch[s][0];
                pre[s]=pos;
            }
            suf[pos]=s;
        }else{
            if(pos<o)insert(ch[o][0],o,pos);
            else insert(ch[o][1],o,pos);
        }
    }
    void del(int o){
        if(pre[o]==0&&suf[o]==0)root[val[o]]=0;
        else{
            if(pre[o]==0){
                splay(o,0);
                fa[root[val[o]]=ch[o][1]]=0;
                ch[o][1]=0;
                pre[suf[o]]=0;
            }else if(suf[o]==0){
                splay(o,0);
                fa[root[val[o]]=ch[o][0]]=0;
                ch[o][0]=0;
                suf[pre[o]]=0;
            }else{
                splay(pre[o],0);
                splay(suf[o],pre[o]);
                ch[suf[o]][0]=0;
                fa[o]=0;
                suf[pre[o]]=suf[o];
                pre[suf[o]]=pre[o];
            }
        }
    }
}sp;
struct fenwicktree{
    int root[maxn+10];
    struct segmenttree{
        int lc[maxn*256+10],rc[maxn*256+10],sum[maxn*256+10],cnt;
        void update(int &o,int l,int r,int p,int v){
            if(!o)o=++cnt;
            if(l==r)sum[o]+=v;
            else{
                int m=mid;
                if(p<=m)update(lc[o],l,m,p,v);
                else update(rc[o],m+1,r,p,v);
                sum[o]=sum[lc[o]]+sum[rc[o]];
            }
        }
        void query(int &o,int l,int r,int ql,int qr,int &qsum){
            if(!o)o=++cnt;
            if(ql<=l&&r<=qr)qsum+=sum[o];
            else{
                int m=mid;
                if(ql<=m)query(lc[o],l,m,ql,qr,qsum);
                if(qr>m)query(rc[o],m+1,r,ql,qr,qsum);
            }
        }
    }seg;
    void add(int x,int p,int v){
        while(x<=n){
            seg.update(root[x],1,n,p,v);
            x+=x&-x;
        }
    }
    int sum(int ql,int qr){
        int ret=0,x=ql;
        while(x){
            seg.query(root[x],1,n,ql,qr,ret);
            x-=x&-x;
        }
        return ret;
    }
}fen;
int main(){
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
        sp.insert(sp.root[val[i]],0,i);
        fen.add(pre[i]+1,i,1);
    }
    char e[2];
    int a,b;
    while(m--){
        scanf("%s%d%d",e,&a,&b);
        a++;
        if(e[0]=='Q')printf("%d\n",fen.sum(a,b));
        else{
            if(suf[a]){
                fen.add(a+1,suf[a],-1);
                fen.add(pre[a]+1,suf[a],1);
            }
            fen.add(pre[a]+1,a,-1);
            sp.del(a);
            sp.insert(sp.root[val[a]=b],0,a);
            if(suf[a]){
                fen.add(pre[a]+1,suf[a],-1);
                fen.add(a+1,suf[a],1);
            }
            fen.add(pre[a]+1,a,1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值