bzoj 2120: 数颜色 带修改莫队算法

题意:有n个位置(n<=10000),初始每个位置有一种颜色,要求资磁两个操作:
R x y 把位置x的颜色变为y
Q x y 求x到y这个区间内出现了多少种不同的颜色

分析:让我们来介绍一下带修改莫队,其实就是三维莫队。
每个询问本来只有二元组(l,r)表示,现在我用一个三元组(l,r,x)表示,意思是对[l,r]进行询问,x表示在此次询问操作之前经过了x次修改操作。
然后和普通莫队一样,第三维也是暴力移动的。即知道(l,r,x)的答案可以知道(l-1,r,x)(l+1,r,x)(l,r-1,x)(l,r+1,x)(l,r,x-1)(l,r,x+1)的答案。
按l所在的块为第一关键字,r所在的块为第二关键字,x为第三关键字排序处理。
块大小需要设为n^2/3,总复杂度是n^5/3,这里就略去复杂度的证明。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define B 250
#define N 10005
using namespace std;

int n,m,q,scha,color[N],t[N*100],tot;
struct data{int l,r,id,ans,x;}w[N];
struct chan{int x,y;}cha[N];

bool cmp(data a,data b)
{
    return (a.l/B<b.l/B||a.l/B==b.l/B&&a.r/B<b.r/B||a.l/B==b.l/B&&a.r/B<b.r/B&&a.x<b.x);
}

void updata(int x,int y)
{
    if (y==1)
    {
        if (!t[color[x]]) tot++;
        t[color[x]]++;
    }else
    {
        t[color[x]]--;
        if (!t[color[x]]) tot--;
    }
}

void change(int x,int y,int l,int r)
{
    if (cha[x].x>=l&&cha[x].x<=r) updata(cha[x].x,-1);
    swap(color[cha[x].x],cha[x].y);
    if (cha[x].x>=l&&cha[x].x<=r) updata(cha[x].x,1);
}

void solve()
{
    updata(1,1);
    for (int i=1,l=1,r=1,x=0;i<=q;i++)
    {
        for (;r<w[i].r;r++)
            updata(r+1,1);
        for (;r>w[i].r;r--)
            updata(r,-1);
        for (;l<w[i].l;l++)
            updata(l,-1);
        for (;l>w[i].l;l--)
            updata(l-1,1);
        for (;x<w[i].x;x++)
            change(x+1,1,l,r);
        for (;x>w[i].x;x--)
            change(x,-1,l,r);
        w[i].ans=tot;
    }
}

bool cmp1(data a,data b)
{
    return a.id<b.id;
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&color[i]);
    for (int i=1;i<=m;i++)
    {
        char ch[2];
        int x,y;
        scanf("%s%d%d",ch,&x,&y);
        if (ch[0]=='R')
        {
            cha[++scha].x=x;
            cha[scha].y=y;
        }else
        {
            w[++q].l=x;
            w[q].r=y;
            w[q].x=scha;
            w[q].id=q;
        }
    }
    sort(w+1,w+q+1,cmp);
    solve();
    sort(w+1,w+q+1,cmp1);
    for (int i=1;i<=q;i++)
        printf("%d\n",w[i].ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值