BZOJ 2120:数颜色

初始给定一个长度为n的数列,给定m次操作,每次要么询问区间[L,R]中有多少种不同的数字,要么修改一个位置的数值。

n<=1e4,m<=1e4,ai<=1e6

询问区间内有多少种不同的数字容易联想到主席树,这边带修改的话那么就树套树了

先回顾一下如果不带修改怎么做,一种做法是对每个位置维护上一个跟当前有相同数字的位置pre[i],然后在区间[L,R]中查询pre值小于L的个数,即相当于把每个数字的贡献放到第一次出现在这个区间里的位置上。

带修改的话我们需要考虑改变一个位置的数值会影响哪些位置的pre,那么我们会影响与原先数值相同的下一个位置的pre、当前位置的pre,以及与新数值相同的下一个位置的pre。

为了快速定位这些位置,其实我们可以对每个数值维护一个set,然后在里面二分找前驱后继,STL大法好,

然后就是相当于单点修改一些位置的pre,然后就是树套树的事了,我这边写的是树状数组套动态开点权值线段树。

二分的时候一些临界的条件需要特判一下,可能这边写得有点丑。

时间复杂度O(Nlog^2N)

#include
using namespace std;
const int maxm=1e6;
const int maxn=1e4;
set<int> s[maxm+5];
int n,m,l,r;
int root[maxn+5];
int pre[maxn+5],a[maxn+5];
int lowbit(int x)
{
    return x&(-x);
}
struct SegmentTree
{
    int lc,rc;
    int cnt;
}b[maxn<<7];
int tot=0;
int build()
{
    int p=++tot;
    b[p].lc=b[p].rc=b[p].cnt=0;
    return p;
}
void pushup(int p)
{
    b[p].cnt=b[b[p].lc].cnt+b[b[p].rc].cnt;
}
void update(int &p,int l,int r,int pos,int val)
{
    if (!p) p=build();
    if (l==r) {
        b[p].cnt+=val;
        return ;
    }
    int mid=(l+r)>>1;
    if (pos<=mid) update(b[p].lc,l,mid,pos,val);
    else update(b[p].rc,mid+1,r,pos,val);
    pushup(p);
}
int getLessCnt(int p,int l,int r,int val)
{
    if (!p) return 0;
    if (l==r) {
        if (lreturn b[p].cnt;
        return 0;
    }
    int mid=(l+r)>>1;
    if (val<=mid) return getLessCnt(b[p].lc,l,mid,val);
    else return b[b[p].lc].cnt+getLessCnt(b[p].rc,mid+1,r,val);
}
char str[10];
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        if (s[a[i]].size()) pre[i]=(*s[a[i]].rbegin());
        s[a[i]].insert(i);
    }
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j+=lowbit(j)) update(root[j],0,n,pre[i],1);
    while (m--) {
        scanf("%s",str);
        if (str[0]=='Q') {
            scanf("%d%d",&l,&r);
            int t=0;
            for (int j=r;j;j-=lowbit(j)) t+=getLessCnt(root[j],0,n,l);
            for (int j=l-1;j;j-=lowbit(j)) t-=getLessCnt(root[j],0,n,l);
            printf("%d\n",t);
        }
        else {
            scanf("%d%d",&l,&r);
            s[a[l]].erase(l);
            set<int>::iterator it1=lower_bound(s[a[l]].begin(),s[a[l]].end(),l);
            if (it1!=s[a[l]].end()) {
                int p1=(*it1);
                if (it1==s[a[l]].begin()) {
                    pre[p1]=0;
                    for (int j=p1;j<=n;j+=lowbit(j)) update(root[j],0,n,l,-1);
                    for (int j=p1;j<=n;j+=lowbit(j)) update(root[j],0,n,0,1);
                }
                else {
                    it1--;
                    pre[p1]=(*it1);
                    for (int j=p1;j<=n;j+=lowbit(j)) update(root[j],0,n,l,-1);
                    for (int j=p1;j<=n;j+=lowbit(j)) update(root[j],0,n,(*it1),1);
                }
            }
            set<int>::iterator it2=lower_bound(s[r].begin(),s[r].end(),l);
            if (it2!=s[r].begin()) {
                it2--;
                for (int j=l;j<=n;j+=lowbit(j)) update(root[j],0,n,pre[l],-1);
                for (int j=l;j<=n;j+=lowbit(j)) update(root[j],0,n,(*it2),1);
                pre[l]=(*it2);
                it2++;
            }
            else {
                for (int j=l;j<=n;j+=lowbit(j)) update(root[j],0,n,pre[l],-1);
                for (int j=l;j<=n;j+=lowbit(j)) update(root[j],0,n,0,1);
                pre[l]=0;
            }
            if (it2!=s[r].end()) {
                int p1=(*it2);
                for (int j=p1;j<=n;j+=lowbit(j)) update(root[j],0,n,pre[p1],-1);
                for (int j=p1;j<=n;j+=lowbit(j)) update(root[j],0,n,l,1);
                pre[p1]=l;
            }
            s[r].insert(l);
            a[l]=r;
        }
    }
    return 0;
}

 

你可能感兴趣的:(BZOJ 2120:数颜色)