• [国家集训队]数颜色 / 维护队列 「带修莫队」


    题目描述

    墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

    1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。

    2、 R P Col 把第P支画笔替换为颜色Col。

    为了满足墨墨的要求,你知道你需要干什么了吗?

    输入格式

    第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。

    第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。

    第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

    输出格式

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

    输入输出样例

    输入 #1

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

    输出 #1

    4
    4
    3
    4
    

    说明/提示

    对于30%的数据,(n,m leq 10000)

    对于60%的数据,(n,m leq 50000)

    对于所有数据,(n,m leq 133333)

    所有的输入数据中出现的所有整数均大于等于 (1) 且不超过 (10^6)

    本题可能轻微卡常数

    思路分析

    • 今天刷莫队的题,看见这道题刚要上去套板子,发现这题带修改啊……于是爬去学习了一下带修莫队

    关于带修莫队

    • 带修莫队其实不难,只是在莫队的基础上稍微做了一丢丢的修改,核心就是增加了一个类似时间轴的东西
    • 我们把查询和修改操作分开离线存储,修改操作该记录什么就记录什么就行了,关键是查询,要在普通莫队的基础上再记录一下此前已经经历了多少次修改
    • 排序的时候也需要进行改动,需要以这个时间轴为第三关键字,像这样:
    inline bool operator <(const query &a)const{
    	return l/len==a.l/len ? (r/len==a.r/len ? tim < a.tim : r < a.r) : l < a.l;//len表示块长
    }
    
    • 然后统计答案的时候先跑一遍莫队的常规操作,然后看一下这时已经修改了多少次,如果修改次数少于应该修改的次数,那就把没有执行的修改操作执行,如果多于应该修改的次数,那就撤销掉多执行的修改操作,部分代码如下:
    while(now < q[i].tim)update(q[i].l,q[i].r,++now);//now代表当前执行的修改次数,q是存储的查询操作,tim就是上面说的时间轴,也就是应该执行的修改次数
    while(now > q[i].tim)update(q[i].l,q[i].r,now--);
    
    • update 里是这样的
    void update(int l,int r,int x){//执行或撤销修改操作,这里通过一个swap巧妙的把两个操作合并在了一起
    	if(l<=mo[x].pos&&mo[x].pos<=r){
    		del(c[mo[x].pos]);
    		add(mo[x].col);
    	}
    	swap(c[mo[x].pos],mo[x].col);
    }
    
    • 然后这题基本解决,还有就是,使用 getchar 时一定要小心!!!,我因为这个浪费好长时间(可见后加的数据有多毒瘤,我之前的都过了)。奥对了,这题块长为 (n^{1/2}) 好像过不去,得改为 (n^{2/3})

    (Code)

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define R register
    #define N 2000010
    using namespace std;
    inline int read(){
    	int x = 0,f = 1;
    	char ch = getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    int n,m,len,c[N],belong[N],c2[N],ans[N],tot1,tot2,cnt[N],nowans;
    struct query{
    	int l,r,id,tim;
    	inline bool operator <(const query &a)const{
    		return l/len==a.l/len ? (r/len==a.r/len ? tim < a.tim : r < a.r) : l < a.l;
    	}
    }q[N];
    struct modify{
    	int pos,col;
    }mo[N];
    void add(int x){
    	cnt[x]++;
    	if(cnt[x]==1)nowans++;
    }
    void del(int x){
    	cnt[x]--;
    	if(!cnt[x])nowans--;
    }
    void update(int l,int r,int x){
    	if(l<=mo[x].pos&&mo[x].pos<=r){
    		del(c[mo[x].pos]);
    		add(mo[x].col);
    	}
    	swap(c[mo[x].pos],mo[x].col);
    }
    int main(){
    	n = read(),m = read();
    	len = pow(n,2.0/3.0);
    	for(R int i = 1;i <= n;i++)c[i] = read();
    	for(R int i = 1;i <= m;i++){
    		char op = getchar();
    		while(op!='Q' && op!='R') op=getchar();//说的就这里
    		int x = read(),y = read();
    		if(op=='Q'){
    			q[++tot1].l = x,q[tot1].r = y,q[tot1].tim = tot2,q[tot1].id = tot1;			
    		}else{
    			mo[++tot2].pos = x,mo[tot2].col = y;
    		}
    	}
    	sort(q+1,q+1+tot1);
    	int l = 1,r = 0,now = 0;
    	for(R int i = 1;i <= tot1;i++){
    		while(l < q[i].l)del(c[l++]);
    		while(l > q[i].l)add(c[--l]);
    		while(r < q[i].r)add(c[++r]);
    		while(r > q[i].r)del(c[r--]);
    		while(now < q[i].tim)update(q[i].l,q[i].r,++now);//多的地方
    		while(now > q[i].tim)update(q[i].l,q[i].r,now--);
    		ans[q[i].id] = nowans;
    	}
    	for(R int i = 1;i <= tot1;i++)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    java实验四
    C# 和SQL Server 类型转换
    C# 通过this关键字来扩展方法
    类型转换
    获取屏幕高度,兼容性问题解决
    LinqToDataTable
    jQuery层级元素选择器
    Javascript 常用系统内置函数
    JSON-JQuery常用技巧
    .net 调用SAP RFC函数获取数据的两种方式
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13831680.html
Copyright © 2020-2023  润新知