2120: 数颜色(带修莫队)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2120

2120: 数颜色

Time Limit: 6 Sec  Memory Limit: 259 MB
Submit: 10514  Solved: 4398
[Submit][Status][Discuss]

Description

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?

Input

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

Output

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

Sample Input

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

Sample Output

4
4
3
4

HINT

 

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。


2016.3.2新加数据两组by Nano_Ape

 

·述大意:

       多个区间询问,询问[l,r]中颜色的种类数。可以单点修改颜色。

·分析:

莫队可以修改?那不是爆炸了吗。

这类爆炸的问题被称为带修莫队(可持久化莫队)。

按照美妙类比思想,可以引入一个“修改时间”,表示当前询问是发生在前Time个修改操作后的。也就是说,在进行莫队算法时,看看当前的询问和时间指针(第三个指针,别忘了l,r)是否相符,然后进行时光倒流或者时光推移操作来保证答案正确性。

·Sort的构造。仅靠原来的sort关键字会使得枚举每个询问都可能因为时间指针移动的缘故要移动n次,总共就n2次,那还不如写暴力。

·为了防止这样的事情发生,再加入第三关键字Tim:

image

·如何理解时间复杂度?

首先,R和Tim的关系就像L和R的关系一样:只有在前者处于同块时,后者才会得到排序的恩赐,否则sort会去满足前者,使得后者开始乱跳。

依旧像上文那样:枚举m个答案,就一个m了。设分块大小为unit。

分类讨论:

①对于l指针,依旧是O(unit*n)

②对于r指针,依旧是O(n*n/unit)

③对于T指针(即Time):

    类比r时间复杂度的计算。我们要寻找有多少个单调段(一个单调段下来最多移动n次)。上文提到,当且仅当两个询问l在同块,r也在同块时,才会对可怜的Tim进行排序。局势明朗。对于每一个l的块,里面r最坏情况下占据了所有的块,所以最坏情况下:有n/unit个l的块,每个l的块中会有n/unit个r的块,此时,在一个r块里,就会出现有序的Tim。所以Tim的单调段个数为:(n/unit)*(n/unit)。每个单调段最多移动n次。

所以:O((n/unit)2*n)

三个指针汇总:O(unit*n+n2/unit+(n/unit)2*n)

image

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1000000+50;
int block;
int a[maxn],b[maxn],ans1[maxn];
int ans=0;
struct Q
{
    int x,y,num,tim;
}q[maxn];
struct U
{
    int x,y;
}tw[maxn];
bool cmp(const Q a,const Q b)
{
    if(a.x/block==b.x/block)
    {
        if(a.y/block==b.y/block) return a.tim<b.tim;
        return a.y/block<b.y/block;
    }
    return a.x/block<b.x/block;
}
void solve(int n,int add)
{
    b[a[n]]+=add;
    if(b[a[n]]==1&&add==1) ans++;
    else if(b[a[n]]==0&&add==-1) ans--;
}
void time_charge(int n,int t)
{
    if(q[n].x<=tw[t].x&&tw[t].x<=q[n].y)
    {
        b[a[tw[t].x]]--;
        if(b[a[tw[t].x]]==0) ans--;
        b[tw[t].y]++;
        if(b[tw[t].y]==1) ans++;
    }
    swap(tw[t].y,a[tw[t].x]);
}
int main()
{
    int N,M,t=0,p1=0,p2=0;
    char c[10];
    scanf("%d%d",&N,&M);
    block=pow(N,0.66666);
    for(int i=1;i<=N;i++) scanf("%d",&a[i]);
    for(int i=0;i<M;i++)
    {
        scanf("%s",c);
        if(c[0]=='Q')
        {
            scanf("%d%d",&q[p1].x,&q[p1].y);
            q[p1].num=p1;
            q[p1].tim=t;
            p1++;
        }
        else
        {
            t++;
            scanf("%d%d",&tw[t].x,&tw[t].y);
        }

    }
    sort(q,q+p1,cmp);
    int L=1,R=0,T=0;
    for(int i=0;i<p1;i++)
    {
        while(R<q[i].y)//往右走
        {
            solve(R+1,1);
            R++;
        }
        while(R>q[i].y)
        {
            solve(R,-1);
            R--;
        }
        while(L<q[i].x)
        {
            solve(L,-1);
            L++;
        }
        while(L>q[i].x)
        {
            solve(L-1,1);
            L--;
        }
        while(T<q[i].tim)
        {
            time_charge(i,T+1);
            T++;
        }
        while(T>q[i].tim)
        {
            time_charge(i,T);
            T--;
        }
        ans1[q[i].num]=ans;
    }
    for(int i=0;i<p1;i++) printf("%d\n",ans1[i]);
    return 0;
}

 

posted @ 2019-05-10 21:47  执||念  阅读(364)  评论(0编辑  收藏  举报