zoukankan      html  css  js  c++  java
  • 【BZOJ2120】数颜色(带修莫队)

    点此看题面

    大致题意: 告诉你(n)只蜡笔的颜色,有两种操作:第一种操作将第(x)只蜡笔颜色改成(y),第二种操作询问区间([l,r])内有多少种颜色的蜡笔。

    考虑普通莫队

    这题目第一眼看上去感觉和【洛谷2709】小B的询问很像,然后就自然而然地会想到用莫队去做。

    但是,在仔细想想,就会发现这道题中有修改,普通的莫队是做不了这样的题目的。

    于是,我们就要用一种新的算法:带修莫队

    带修莫队

    如果你会带修莫队,那么这题应该就是一道简单的板子题了。

    对于每一个询问,记录下在这次询问前进行了几次修改

    而对于每一个修改,记录下当前修改元素修改前的值

    这样,就可以对于每一个询问,将没有执行的修改执行掉,将多执行的修改撤销掉即可。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define LL long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
    #define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
    #define N 50000
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
    using namespace std;
    int n,m,blo,q_num,o_num,col[N+5],lst[N+5],pos[N+5],res[N+5],cnt[1000005];
    struct Query//记录询问
    {
        int l,r,pos,k;//l和r记录询问区间,pos记录这个询问的编号,方便最后答案的输出,k记录这个询问前执行的操作数
    }q[N+5];
    struct Operation//记录操作
    {
        int x,y,z;//x记录修改的元素的编号,y记录要修改成的值,z记录修改前的值,方便撤销
    }o[N+5];
    inline void read(int &x)
    {
        x=0;static char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    inline void read_alpha(char &x)
    {
        while(!isalpha(x=tc()));
    }
    inline void write(int x)
    {
        if(x>9) write(x/10);
        pc(x%10+'0');
    }
    inline bool cmp(Query x,Query y)//排序
    {
        if(pos[x.l]^pos[y.l]) return pos[x.l]<pos[y.l];//如果两个询问的l不在同一块内
        if(pos[x.r]^pos[y.r]) return pos[x.r]<pos[y.r];//如果两个询问的r不在同一块内
        return x.k<y.k;//比较两个询问执行操作的个数
    }
    int main()
    {
        register int i,j,x,y;register char ch;
        for(read(n),read(m),blo=pow(n,2.0/3),i=1;i<=n;++i) read(col[i]),lst[i]=col[i],pos[i]=(i-1)/blo+1;
        for(i=1;i<=m;++i)
        {
            read_alpha(ch),read(x),read(y);
            if(ch^'R') q[++q_num].l=x,q[q_num].r=y,q[q[q_num].pos=q_num].k=o_num;
            else o[++o_num].x=x,o[o_num].y=y,o[o_num].z=lst[x],lst[x]=y;
        }
        register int L=0,R=0,K=0,ans=0;//L和R记录当前答案的区间,K记录当前执行的操作数,ans记录当前的答案,初始化全为0
        for(sort(q+1,q+q_num+1,cmp),i=1;i<=q_num;++i)
        {
            while(K<q[i].k)//如果当前执行的操作数少于当前询问执行的操作数 
            {
                ++K;
                if(L<=o[K].x&&o[K].x<=R)//如果修改的点在L到R区间内 
                {
                    if(!--cnt[col[o[K].x]]) --ans;//如果删除了原先的颜色使得这种颜色的个数为0,那么将ans减1
                    if(!cnt[o[K].y]++) ++ans;//如果加上了新的颜色使得颜色个数增加了1,那么将ans加1
                }
                col[o[K].x]=o[K].y;//修改颜色
            }
            while(K>q[i].k)//如果当前执行掉操作数多余当前询问执行的操作数
            {
                if(L<=o[K].x&&o[K].x<=R)//如果修改的点在L到R区间内
                {
                    if(!--cnt[col[o[K].x]]) --ans;//如上
                    if(!cnt[o[K].z]++) ++ans;//如上
                }
                col[o[K].x]=o[K].z,--K;//如上
            }
            while(R<q[i].r) if(!cnt[col[++R]]++) ++ans;//如果当前的R小于当前询问的r,那么将R向右移
            while(L>q[i].l) if(!cnt[col[--L]]++) ++ans;//如果当前的L大于当前询问的l,那么将L向左移
            while(R>q[i].r) if(!--cnt[col[R--]]) --ans;//如果当前的R大于当前询问的r,那么将R向左移
            while(L<q[i].l) if(!--cnt[col[L++]]) --ans;//如果当前的L小于当前询问的l,那么将L向右移
            res[q[i].pos]=ans;//记录答案
        }
        for(i=1;i<=q_num;++i) write(res[i]),pc('
    ');
        return fwrite(pp,1,pp_,stdout),0;
    }
    
  • 相关阅读:
    关于编程
    Python的内建sort方法

    Elgg设置SMTP验证发送邮件教程
    ThinkPHP 和 UCenter接口的冲突
    mac下终端iTerm2配置
    自动化 Amazon EBS 快照生命周期
    AWS Certified Solutions Architect Associate 学习笔记1
    实例存储生命周期 Instance store
    可触发 Lambda 函数的 CloudFront 事件
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2120.html
Copyright © 2011-2022 走看看