洛谷 P1903 [国家集训队]数颜色 解题报告

P1903 [国家集训队]数颜色

题目描述

墨墨购买了一套(N)支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

1、Q L R代表询问你从第(L)支画笔到第(R)支画笔中共有几种不同颜色的画笔。

2、 R P Col 把第(P)支画笔替换为颜色(Col)

为了满足墨墨的要求,你知道你需要干什么了吗?

输入输出格式

输入格式:

第1行两个整数(N)(M),分别代表初始画笔的数量以及墨墨会做的事情的个数。

第2行(N)个整数,分别代表初始画笔排中第i支画笔的颜色。

第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

输出格式:

对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

说明

对于100%的数据,N≤50000,M≤50000,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。


正解是待修莫队,不过树套树复杂度更优秀(常数就不一定了),就拿树套树过了这个题

把一个颜色的笔放到一个set里,以位置为关键字,同时更新(pre)数组,代表前一个相同颜色的位置(特别的,如果没有,为0)

先不考虑修改,我们发现询问([l,r])有多少不同颜色等价于询问有多少个(pre)数组小于(l),我们可以拿权值线段树解决这个问题

然后对于修改,我们只需要在外面套上树状数组就可以啦


Code:

#include <cstdio>
#include <algorithm>
#include <set>
#define ls ch[now][0]
#define rs ch[now][1]
using namespace std;
const int N=2e5+10;
int ch[N*18][2],sum[N*18],root[N],tot;
int a[N],b[N],pre[N],n,m,k;
void updata(int now){sum[now]=sum[ls]+sum[rs];}
void change(int &now,int l,int r,int pos,int delta)
{
    if(!now) now=++tot;
    if(l==r) {sum[now]+=delta;return;}
    int mid=l+r>>1;
    if(pos<=mid) change(ls,l,mid,pos,delta);
    else change(rs,mid+1,r,pos,delta);
    updata(now);
}
int query(int now,int l,int r,int pos)
{
    if(!now||l==r) return 0;
    int mid=l+r>>1;
    if(pos<=mid) return query(ls,l,mid,pos);
    else return sum[ls]+query(rs,mid+1,r,pos);
}
void modify(int x,int pos,int delta)
{
    while(x<=n) change(root[x],0,n-1,pos,delta),x+=x&-x;
}
int ask(int x,int rk)
{
    int ans=0;
    while(x)
        ans+=query(root[x],0,n-1,rk),x-=x&-x;
    return ans;
}
struct node{int op,x,y;}opt[N];
set <int > rb[N];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    m=n;char c[3];
    for(int i=1;i<=k;i++)
    {
        scanf("%s%d%d",c,&opt[i].x,&opt[i].y);
        opt[i].op=(c[0]=='R');
        if(opt[i].op) a[++m]=opt[i].y;
    }
    sort(a+1,a+1+m);
    m=unique(a+1,a+1+m)-a-1;
    for(int i=1;i<=n;i++) b[i]=lower_bound(a+1,a+1+m,b[i])-a;
    for(int i=1;i<=k;i++) if(opt[i].op) opt[i].y=lower_bound(a+1,a+1+m,opt[i].y)-a;
    for(int i=1;i<=n;i++)
    {
        if(!rb[b[i]].empty())
        {
            set <int>::iterator it=rb[b[i]].end();
            it--;
            pre[i]=*it;
        }
        rb[b[i]].insert(i);
    }
    for(int i=1;i<=n;i++)
        for(int j=i-(i&-i)+1;j<=i;j++)
            change(root[i],0,n-1,pre[j],1);
    for(int i=1;i<=k;i++)
    {
        if(opt[i].op)
        {
            int co=opt[i].y,pos=opt[i].x,pr;
            set <int >::iterator it=rb[b[pos]].find(pos);//找到颜色位置
            it++;
            if(it!=rb[b[pos]].end())//改它的后继
            {
                modify(*it,pos,-1);
                modify(*it,pre[*it]=pre[pos],1);
            }
            rb[b[pos]].erase(pos);//删掉
            rb[b[pos]=co].insert(pos);//加入且改自己颜色
            it=rb[co].find(pos);
            if(it!=rb[co].begin()) it--,pr=*it,it++;
            else pr=0;//找到pre
            modify(pos,pre[pos],-1);//改自己pre
            modify(pos,pre[pos]=pr,1);
            it++;
            if(it!=rb[b[pos]].end())//寻找后面
            {
                modify(*it,pre[*it],-1);
                modify(*it,pre[*it]=pos,1);
            }
        }
        else
            printf("%d
",ask(opt[i].y,opt[i].x)-opt[i].x+1);
    }
    return 0;
}


2018.9.4

原文地址:https://www.cnblogs.com/butterflydew/p/9588311.html