【JZOJ 4594】【UVa 12345】Dynamic len 带修莫队

【JZOJ 4594】【UVa 12345】Dynamic len

题意:

两种操作: M x y 将 a[x]的值修改为 y ,Q  x  y 求区间 [x,y-1] 有几个不相同的数字。注意题目中的下标从0开始。

输入的时候我们从下标 1 开始,修改时需要先将 坐标加1 ,查询时的区间为 [++x , y]

#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<bitset>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
struct node
{
    int l,r;
    int time;
    int id;
}t[maxn];
struct Change
{
    int last;   //此次修改时原来的值,用于还原数组
    int x;    //修改位置
    int val;    //修改为那个值
}c[maxn];
int n,m,a[maxn];
int block;
int numq,numc; //查询、修改操作的次数
ll ans,cnt[maxn],res[maxn],last[maxn],flag[maxn];
bool cmp(node a,node b){//排序
    if( (a.l/block)==(b.l/block) ){//当左端点位于同一个块时
        if( (a.r/block)==(b.r/block) )//当右端点位于同一个块时
            return a.time<b.time;
        else
            return a.r<b.r;
    }
    else//当左端点不位于同一个块时
        return a.l<b.l;
    //return (a.l/block)^(b.l/block) ? a.l<b.l : ( ((a.r/block)^(b.r/block))?a.r<b.r:a.time<b.time );
}
void add(int x)  //统计新的
{
    if(cnt[a[x]]==0)  //如果这个点没出现过
        ans++;
    cnt[a[x]]++;
    flag[x] = 1;  //标记该点在记录中
}
void del(int x)  //减去旧的
{
    cnt[a[x]]--;
    if(cnt[a[x]]==0) //这个点只出现过一次
        ans--;
    flag[x] = 0;  //标记该点不在记录中
}
void push(int x)   //如果这个点原本有,那就删掉,没有就加上
{
    if(flag[x])       //这个操作是因为后面有反向操作
        del(x);
    else
        add(x);
}
void change(int x,int v)  
{
    if(flag[x])  // 改变这个点的值,如果原本有值,删掉后赋值,没有就直接赋值
    {
        del(x);
        a[x] = v;
        add(x);
    }
    else
        a[x] = v;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        ans = 0;
        numq = numc = 0;
        memset(cnt,0,sizeof(cnt));
        block = pow(n,0.66666);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            last[i] = a[i];
        }
        for(int i=1;i<=m;i++)
        {
            char ch[10];
            scanf("%s",ch);
            if(ch[0]=='Q')  //查询
            {
                numq++;   //查询操作次数+1
                scanf("%d%d",&t[numq].l,&t[numq].r);
                t[numq].l++;
                t[numq].id = numq;
                t[numq].time = numc;
            }
            else  //修改
            {
                numc++;   //修改操作次数+1
                scanf("%d%d",&c[numc].x,&c[numc].val);
                c[numc].x++;
                c[numc].last = last[c[numc].x];  //由于修改的值可能要修改回去,所以要记录修改前的值
                last[c[numc].x] = c[numc].val;
            }
        }
        sort(t+1,t+1+numq,cmp);  //对询问进行排序
        int l=1,r=0,ti=0; //左右指针与时间轴
        for(int i=1;i<=numq;i++)
        {
            while(l>t[i].l)  push(--l); //[l-1,r,time]
            while(l<t[i].l)  push(l++); //[l+1,r,time]
            while(r<t[i].r)  push(++r); //[l,r+1,time]
            while(r>t[i].r)  push(r--); //[l,r-1,time]
            while(ti<t[i].time)  ti++,change(c[ti].x,c[ti].val); //[l,r,time+1]   把这个询问时间前的修改全部做了 

            while(ti>t[i].time)  change(c[ti].x,c[ti].last),ti--; //[l,r,time-1]  若是询问在当前时间后面,那么就要把修改了的改回去
            res[t[i].id] = ans;
        }
        for(int i=1;i<=numq;i++)
            printf("%lld\n",res[i]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值