国家集训队 数颜色 [莫队]

题目描述

墨墨购买了一套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支画笔中共有几种不同颜色的画笔。

输入输出样例

输入样例#1:

6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6

输出样例#1:

4
4
3
4

说明

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

本题可能轻微卡常数

题解

  • 带修改莫队裸题

  • 学过莫队的人都知道,普通的莫队是不带修改操作的,但是后人在普通莫队的基础上,又加以改进,发明了带修改莫队,它的思想很简单易懂

  • 只有当出现查询操作时,前面的修改操作才有用,也就是说查询和修改两个操作是彼此独立的,因此,我们可以分别记录一下,哪些是查询,哪些是修改,然后普通莫队就行

  • 这样就够了吗?

  • 当然不行,我们还要记录一下到当前为止我们已经修改了几次,如果我们修改次数大于查询的序号,即改多了,我们就改回去,改少了就改过去

  • 举个例子,比如当前修改了3次,而最近的查询操作在第五次,我们就还需要进行第4,5次修改操作

  • 如果之前学过普通莫队的看这份代码应该没什么问题

  • 洛谷数据加强了,开(sqr(n))会T飞,开(7*sqr(n))快很多

Code

#include<bits/stdc++.h>
#define in(i) (i=read())
using namespace std;
int read() {
    int ans=0,f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0'; i=getchar();}
    return ans*f;
}
int n,m,qnum,cnum,tot;
int c[1000010],cnt[1000010],ans[1000010],where[1000010];
struct change {
    int pos,col;
}a[1000010];
struct query {
    int l,r,id,pre;
}e[1000010];
bool cmp(query a,query b) {
    if(where[a.l]!=where[b.l]) return where[a.l]<where[b.l];
    if(where[a.r]!=where[b.r]) return where[a.r]<where[b.r];
    return a.pre<b.pre;
}
void add(int x) {
    cnt[c[x]]++;
    if(cnt[c[x]]==1) tot++;
}
void remove(int x) {
    cnt[c[x]]--;
    if(!cnt[c[x]]) tot--;
}
void work(int now,int i) {
    if(a[now].pos>=e[i].l && a[now].pos<=e[i].r) {
        if(--cnt[c[a[now].pos]]==0) tot--;
        if(++cnt[a[now].col]==1) tot++;
    }
    swap(a[now].col,c[a[now].pos]);
}
int main()
{
    in(n); in(m);
    int block=sqrt(n)*7;
    for(int i=1;i<=n;i++) {in(c[i]); where[i]=(i-1)/block+1;}
    for(int i=1;i<=m;i++) {
        char op; cin>>op;
        if(op=='Q') {
            in(e[++qnum].l); in(e[qnum].r);
            e[qnum].id=qnum;
            e[qnum].pre=cnum;
        }
        else {
            in(a[++cnum].pos); in(a[cnum].col);
        }
    }
    sort(e+1,e+1+qnum,cmp);
    for(int i=1,curl=1,curr=0,now=0;i<=qnum;i++) {
        int l=e[i].l, r=e[i].r;
        while(curl<l) remove(curl++);
        while(curl>l) add(--curl);
        while(curr<r) add(++curr);
        while(curr>r) remove(curr--);
        while(now<e[i].pre) work(++now,i);
        while(now>e[i].pre) work(now--,i);
        ans[e[i].id]=tot;
    }
    for(int i=1;i<=qnum;i++) printf("%d
",ans[i]);
    return 0;
}

边界问题

最开始now=0,所以先增再执行
最后此时now不符合条件,所以先执行再减

博主蒟蒻,随意转载.但必须附上原文链接

http://www.cnblogs.com/real-l/

原文地址:https://www.cnblogs.com/real-l/p/9210936.html