数颜色

Cici有一些彩盒,Cici喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续彩盒中,不同颜色的彩盒有多少。当然,Cici有时候会替换队列中某个彩盒的颜色。
Input
第一行包含两个整数N和M。
第二行N个整数,表示初始队列中彩盒的颜色。
接下来M行
每行的形式为“Q L R”或“R x c”,
“Q L R”表示Cici想知道从队列第L个彩盒到第R个彩盒中,一共有多少不同颜色的彩盒,
“R x c”表示Cici把x位置上的彩盒换成了c颜色。
有1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,
小朋友Cici不会修改超过1000次,所有颜色均用1到10^6的整数表示。

Output
对于每个Q操作,输出一行表示询问结果。
Sample Input
2 3
1 2
Q 1 2
R 1 2
Q 1 2
Sample Output
2
1

Sol:

此题主要是维护的问题,当改变一个位置的color,则影响两个color的前后关系。

用pre[i]表示第i个元素的前一个相同元素的位置,
问题变成在区间[l,r]里找大于l的数的个数,
(具体什么意思去其他地方看)默认值为最大值。
用ppp[i]表示颜色i最后出现的位置(针对这道题颜色最多1e6种,建一个1e6的数组即可)
color[i]表示第i个位置是什么颜色

========================================================================

本题要统计某个区间,出现过多少种不同的color.
于是
我们可以维护下某个区间[x,y]上每个点,它的color,上一次出现的位置pre[i]
当我们走到一个点i,它的pre[i]<x,则在当前出现了一次,上一次出现位置又小于x,则说明是第一次被统计到。
根据上面这个思路,我们可以分块处理。
离散块暴力来找,整块操作时,可以二分来找当前区间中有多少个数字是小于x的。
修改操作
还要记下某个color,设为a最后出现的位置ppp[a],形成一个链表pre[a]..ppp[a]。
当将x位置上的color,换成另一种color时
先对删除color的点进行讨论
1:为最后一个点
2:不为最后一个点

再加换上去的color进行讨论,设其最后的位置为c
形为pre[c]............c
分类如下
1:在c的右边
2:在pre[c]与c之间
3:在pre[c]的左边形如x.......pre[c]
此时就要找到一个位置c',形成pre[c'].....x...c'

//https://blog.csdn.net/shisuan1/article/details/81268270

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define inf 0x3f3f3f3f
using namespace std;
int a[205][205];
int pre[200005];
int color[200005];
int ppp[1000005];
int n,m,l,r;
void reset(int x)
{
    int p=(x-1)/l; //x所在的块 
    for(int j=1;j<=l;j++) 
    {
            a[p][j]=pre[p*l+j];
    }
    sort(a[p]+1,a[p]+l+1);
}
void build()
{
    for(int i=0;i<r;i++) //枚举块的个数 
    {
        for(int j=1;j<=l;j++) //枚举块大小 
        {
            a[i][j]=pre[i*l+j];
        }
        sort(a[i]+1,a[i]+l+1);
    }
}
int search(int x,int y)
{
    int ans=0;
    int p=(x-1)/l+1;
    int q=(y-1)/l;
    if(p>=q)
    {
       for(int i=x;i<=y;i++) //枚举位置 
          if(pre[i]<x) 
		  //如果这个位置的color上一次出现在x的左边,则当前出现过一次 
		  //如果是大于x,则说明在[x,y]之间出现过不止一次 
             ans++;
       return ans;
    }
    for(int i=x;i<=p*l;i++)
         if(pre[i]<x)
            ans++;
    for(int i=q*l+1;i<=y;i++)
         if(pre[i]<x)
            ans++;
    for(int i=p;i<q;i++)
    //对于中间的块,枚举每一块,看它里面存的数字,有多少是<x的 
    {
        ans+=lower_bound(a[i]+1,a[i]+l+1,x)-a[i]-1;
		//后面有个减1,于是就是找<x的了 
    }
    return ans;
}
void change(int x,int y)//位置x,颜色y.
{
    int p=color[x]; //记下x位置上从前的color
    color[x]=y;     //更新为当前的color
    int c=ppp[p];   //c为x这个位置上从前color的最后出现的位置
    if(c==x)        //如果这个位置等于x,即最后一个点没有了
    {
          ppp[p]=pre[c];  //则最后出现的位置变成pre[c]
    }
    else   //说明被改变color的不是其最后一个位置
    {
        while(pre[c]!=x) //不断去找这个位置
             c=pre[c];
        pre[c]=pre[x];   //形如pre(x)......x......c,因为x这个位置去掉了,于是pre[c]=pre[x]
        reset(c);      //对c所在的段进行重构
    }
    
    c=ppp[y];  //c代表color为y的最后出现的位置
    if(c<x)    //如果这个位置小于x,形如c.....x则x是这个color的最后出现位置
    {
        pre[x]=c; 
        ppp[y]=x;
        reset(x);
    }
    else
    if(pre[c]<x)  //形如pre[c]....x......c
    {
        pre[x]=pre[c];
        pre[c]=x;
        reset(x);
        reset(c);
    }
    else  //形如x.....pre[c]
    {
        while(pre[c]>x)
        {
        c=pre[c];
        }
        //确定一个c的位置pre[c]....x....c
        pre[x]=pre[c];
        pre[c]=x;
        reset(x);
        reset(c);
    }
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    memset(ppp,0,sizeof(ppp));
    memset(pre,inf,sizeof(pre));
    pre[0]=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&color[i]);
        if(ppp[color[i]]!=0) //ppp[i]表示颜色i最后出现的位置
              pre[i]=ppp[color[i]];
        else
              pre[i]=0;   //pre[i]表示颜色i上一次出现的位置
        ppp[color[i]]=i;
    }
    l=floor(sqrt(n)); //块的大小 
    if(n%l>0)
        r=n/l+1; //块的个数 
    else
        r=n/l;
    
    build();
    int x,y;
    char c[10];
    for(int i=0;i<m;i++)
    {
        scanf("%s%d%d",c,&x,&y);
        if(c[0]=='Q')
                 printf("%d\n",search(x,y));
        else
                 change(x,y);
    }
    return 0;
}

  

 带修改的莫队算法

https://www.cnblogs.com/RabbitHu/p/MoDuiTutorial.html

 

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define space putchar(' ')
#define enter putchar('\n')
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) x = -x, putchar('-');
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 10005, M = 1000005, B = 464;
int n, m, pl = 1, pr = 0, cur, res, ans[N], a[N], cnt[M];
int idxC, idxQ, tim[N], pos[N], val[N], pre[N];
#define bel(x) (((x) - 1) / B + 1)
struct query 
{
    int id, tim, l, r;
    bool operator < (const query &b) const 
	{
        if(bel(l) != bel(b.l)) return l < b.l;
        if(bel(r) != bel(b.r)) return r < b.r;
        return id < b.id;
    }
} q[N];
void change_add(int cur)
{
    if(pos[cur] >= pl && pos[cur] <= pr)
	{
        cnt[a[pos[cur]]]--;
        if(!cnt[a[pos[cur]]]) 
		   res--;
    }
    pre[cur] = a[pos[cur]];//记下从前的color 
    a[pos[cur]] = val[cur]; //换上新的color 
    if(pos[cur] >= pl && pos[cur] <= pr)
	{
        if(!cnt[a[pos[cur]]]) 
		    res++;
        cnt[a[pos[cur]]]++;
    }
}
void change_del(int cur)
{
    if(pos[cur] >= pl && pos[cur] <= pr)
	{
        cnt[a[pos[cur]]]--;
        if(!cnt[a[pos[cur]]]) res--;
    }
    a[pos[cur]] = pre[cur];
    if(pos[cur] >= pl && pos[cur] <= pr)
	{
        if(!cnt[a[pos[cur]]]) res++;
        cnt[a[pos[cur]]]++;
    }
}
void change(int now)
{
    while(cur < idxC && tim[cur + 1] <= now) 
    //cur代表目前的修改操作进行到一个了,如果还没到当前询问的时间点
	//则有些修改操作还没有做,加进来 
	      change_add(++cur);
    while(cur && tim[cur] > now) 
    //也有可能是多做了修改操作,于是删除这些修改操作 
	      change_del(cur--);
}
void add(int p)
{
    if(!cnt[a[p]]) 
	    res++;
    cnt[a[p]]++;
}
void del(int p)
{
    cnt[a[p]]--;
    if(!cnt[a[p]]) 
	   res--;
}
bool isQ()
{
    char op[2];
    scanf("%s", op);
    return op[0] == 'Q';
}
int main()
{
    read(n), read(m);
    for(int i = 1; i <= n; i++) 
	    read(a[i]);
    for(int i = 1; i <= m; i++)
	{
        if(isQ())  //查询操作 
        {
		     idxQ++;
			 q[idxQ].id = idxQ;
			 q[idxQ].tim = i;
			 read(q[idxQ].l);
			 read(q[idxQ].r);
	    }
		else      //修改操作 
		     {    
			          tim[++idxC] = i; //第idxc个修改操作发生的时间 
					  read(pos[idxC]); //修改的位置 
					  read(val[idxC]); //修改后的color 
			 }  
	}
    sort(q + 1, q + idxQ + 1);
    for(int i = 1; i <= idxQ; i++)
	{
        change(q[i].tim); 
		//以当前这个查询为基准,是否有修改操作没有做,或者多做了 
        while(pl > q[i].l) add(--pl);
        while(pr < q[i].r) add(++pr);
        while(pl < q[i].l) del(pl++);
        while(pr > q[i].r) del(pr--);
        ans[q[i].id] = res;
    }
    for(int i = 1; i <= idxQ; i++)
        write(ans[i]), enter;
    return 0;
}

  

posted @ 2020-04-29 11:19  我微笑不代表我快乐  阅读(255)  评论(0编辑  收藏  举报