对于不会树套树、主席树的本蒟蒻,还是老老实实的用莫队做吧....

其实这题跟普通莫队差不了多远,无非就是有了一个时间,当我们按正常流程排完序后,按照基本的莫队来,做莫队时每次循环对于这一次操作,我们在结构体中记录一下这次操作前有多少个改值操作,然后将当前的ans和每个点的颜色信息更新至当前队列(此队列不是莫队,是原队列)的状态再移动l,r两指针。

简单点说:

设当前询问为a,下一个询问为b,我们已知a,要求b。

首先我们像普通莫队一样转移左右端点。

这时候我们可能会发现a和b的经历的修改次数不同

假如a较b少修改了p次,那我们就把这p次修改一个一个从前往后暴力地加上去。假如a较b多修改了q次,那我们就把这q次修改从后往前还原掉。

还要注意一点:

带修改的莫队的询问排序方法为:

  • 第一关键字:左端点所在块编号,从小到大排序。
  • 第二关键字:右端点所在块编号,从小到大排序。
  • 第三关键字:经历的修改次数。也可以说是询问的先后,先询问的排前面。

注意的几点:

  • 时间上的优化:将块的大小从sqrt(n)改为n的二分之三次方
  • 可修改莫队只用于单点修改,区间修改的题目就算了吧...
  • 复杂度大约为O(n的五分之三次方)
  • 一样是离线操作

但是具体怎么操作呢?

我们先看一下结构体:

  1. struct Node{
  2. int l,r,c,id;
  3. bool operator < (const Node a)const {
  4. if(l/Be==a.l/Be){
  5. if(r/Be==a.r/Be)return id<a.id;
  6. return r<a.r;
  7. }return l<a.l;
  8. }
  9. }q[N];

上面的重载运算符就是按照上文的排序方式来写的。

结构体中l,r分别是询问的左右端点,c是该询问之前的修改操作次数,id是这个操作的位置。

再来比较一下普通的莫队操作和可修改的莫队操作:

更新ans的函数(一样的):

  1. inline void Add(int x){if(!sum[x])ans++;sum[x]++;}
  2. inline void Del(int x){sum[x]--;if(!sum[x])ans--;}

普通莫队:

  1. for(register int i=1;i<=m;++i){
  2. while(l>q[i].l)l--,Add(val[l]);
  3. while(r<q[i].r)r++,Add(val[r]);
  4. while(l<q[i].l)Add(val[l]),l++;
  5. while(r>q[i].r)Add(val[r]);,r--;
  6. Ans[q[i].id]=ans;
  7. }

可修改莫队:

  1. for(register int i=0;i<c1;++i){
  2. for(;lst<q[i].c;lst++){
  3. if(l<=Q[lst][0]&&Q[lst][0]<=r)
  4. Del(Q[lst][1]),Add(Q[lst][2]);
  5. val[Q[lst][0]]=Q[lst][2];
  6. }
  7. for(;lst>q[i].c;lst--){
  8. if(l<=Q[lst-1][0]&&Q[lst-1][0]<=r)
  9. Del(Q[lst-1][2]),Add(Q[lst-1][1]);
  10. val[Q[lst-1][0]]=Q[lst-1][1];
  11. }
  12. for(++r;r<=q[i].r;r++)Add(val[r]);
  13. for(--r;r>q[i].r;r--)Del(val[r]);
  14. for(--l;l>=q[i].l;l--)Add(val[l]);
  15. for(++l;l<q[i].l;l++)Del(val[l]);
  16. Ans[q[i].id]=ans;
  17. }

(注:可修改莫队的下面四个for循环,是在我的心态极不好的时候才从让我WA了几遍的while循环改过来的,但是后面才发现不是while的原因......贴代码过来时不想改了)

我们发现,可修改莫队多了一段:

  1. for(;lst<q[i].c;lst++){
  2. if(l<=Q[lst][0]&&Q[lst][0]<=r)
  3. Del(Q[lst][1]),Add(Q[lst][2]);
  4. val[Q[lst][0]]=Q[lst][2];
  5. }
  6. for(;lst>q[i].c;lst--){
  7. if(l<=Q[lst-1][0]&&Q[lst-1][0]<=r)
  8. Del(Q[lst-1][2]),Add(Q[lst-1][1]);
  9. val[Q[lst-1][0]]=Q[lst-1][1];
  10. }

没错!这就是这段话:

假如a较b少修改了p次,那我们就把这p次修改一个一个从前往后暴力地加上去。假如a较b多修改了q次,那我们就把这q次修改从后往前还原掉。

上面的if是来判断这个改色操作是否在当前莫队范围中,在的话肯定要维护一下当前的莫队ans值嘛,但是不在范围中的话,直接改颜色不就好了......(貌似并没有那么难操作诶)


  • 输入时的操作:

用一个变量c1表示有多少个询问操作,c2表示有多少个更改操作。

  • 用结构体保存每次询问操作的相关信息
  • 用一个二维数组保存每次更改操作的相关信息

代码:

  1. for(int i=1,a,b;i<=m;i++)
  2. if(scanf("%s",opt),read(a),read(b),opt[0]=='Q')
  3. q[c1].l=a,q[c1].r=b,q[c1].id=c1,q[c1].c=c2,c1++;
  4. else Q[c2][0]=a,Q[c2][1]=C[a],Q[c2][2]=C[a]=b,c2++;
  • Q[i][0] 表示第i次操作需更改的位置
  • Q[i][1] 表示第i次操作更改位置的现在颜色
  • Q[i][2] 表示第i次操作要改成的颜色
  • 注意,Q[i][1]在之前的更改操作中可能已经被改过,而我们有要记录这个点现在的值,所以我维护了一个C数组来随时记录每次操作的变化,原序列数组为val数组,显然是不能用原序列数组来记录的(不然到莫队时就乱了)

说了这么多,总算是要贴AC代码了。

AC代码(请原谅我鬼畜的码风,压行压疯了...):

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define inf 0x3f3f3f3f
  4. #define A printf("A")
  5. #define P(x) printf("V %d V",x);
  6. #define S 1000003
  7. using namespace std;
  8. const int N=5e4+5;
  9. template<typename _Tp>inline void read(_Tp&dig){
  10. char c;dig=0;
  11. while(c=getchar(),!isdigit(c));
  12. while(isdigit(c))dig=dig*10+c-'0',c=getchar();
  13. }
  14. int n,m,Be,c1,c2,ans,C[N],val[N],Ans[N],sum[S],Q[N][3];
  15. struct Node{
  16. int l,r,c,id;
  17. bool operator < (const Node a)const {
  18. if(l/Be==a.l/Be){
  19. if(r/Be==a.r/Be)return id<a.id;
  20. return r<a.r;
  21. }return l<a.l;
  22. }
  23. }q[N];char opt[10];
  24. inline void Add(int x){if(!sum[x])ans++;sum[x]++;}
  25. inline void Del(int x){sum[x]--;if(!sum[x])ans--;}
  26. int main(){
  27. read(n);read(m);Be=pow(n,(double)2/(double)3);
  28. for(register int i=1;i<=n;++i)read(val[i]),C[i]=val[i];
  29. for(int i=1,a,b;i<=m;i++)
  30. if(scanf("%s",opt),read(a),read(b),opt[0]=='Q')
  31. q[c1].l=a,q[c1].r=b,q[c1].id=c1,q[c1].c=c2,c1++;
  32. else Q[c2][0]=a,Q[c2][1]=C[a],Q[c2][2]=C[a]=b,c2++;
  33. sort(q,q+c1),Add(val[1]);int l=1,r=1,lst=0;
  34. for(register int i=0;i<c1;++i){
  35. for(;lst<q[i].c;lst++){
  36. if(l<=Q[lst][0]&&Q[lst][0]<=r)
  37. Del(Q[lst][1]),Add(Q[lst][2]);
  38. val[Q[lst][0]]=Q[lst][2];
  39. }
  40. for(;lst>q[i].c;lst--){
  41. if(l<=Q[lst-1][0]&&Q[lst-1][0]<=r)
  42. Del(Q[lst-1][2]),Add(Q[lst-1][1]);
  43. val[Q[lst-1][0]]=Q[lst-1][1];
  44. }
  45. for(++r;r<=q[i].r;r++)Add(val[r]);
  46. for(--r;r>q[i].r;r--)Del(val[r]);
  47. for(--l;l>=q[i].l;l--)Add(val[l]);
  48. for(++l;l<q[i].l;l++)Del(val[l]);
  49. Ans[q[i].id]=ans;
  50. }for(register int i=0;i<c1;++i)printf("%d\n",Ans[i]);
  51. return 0;
  52. }

提交上去,哇,~跑的真快~慢死了!(好吧,我觉的还需优化一下......)

** 参考隔壁机房 叉ZY 大佬的文章,在此鸣谢叉ZY大佬

~我居然因为少了个等于号调了一个多小时.....~

题解 洛谷P1903/BZOJ2120【[国家集训队]数颜色 / 维护队列】的更多相关文章

  1. bzoj2120 / P1903 [国家集训队]数颜色 / 维护队列(带修改莫队)

    P1903 [国家集训队]数颜色 / 维护队列 带修改的莫队 在原有指针$(l,r)$上又添加了时间指针$t$ 贴一段dalao的解释 带修改的莫队,和原版莫队相比,多了一个时间轴 原版莫队是将区间( ...

  2. P1903 [国家集训队]数颜色 / 维护队列(莫队区间询问+单点修改)

    题目链接:https://www.luogu.org/problemnew/show/P1903 题目大意:中文题目 具体思路:莫队单点修改+区间询问模板题,在原来的区间询问的基础上,我们要记录当前这 ...

  3. 洛谷 P1903 [国家集训队]数颜色 / 维护队列

    墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. \(Q\) \(L\) \(R\) 代表询问你从第L支画笔到第R支画笔中共有几种不同 ...

  4. 洛谷P1903 [国家集训队]数颜色 / 维护队列 ( 带 修 )

    题意:有两种操作: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2. R P Col 把第P支画笔替换为颜色Col. 对每个1操作 输出答案: 带修莫队 模板题 (加 ...

  5. 洛谷 P1903 [国家集训队]数颜色 / 维护队列 带修莫队

    题目描述 墨墨购买了一套\(N\)支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: \(1\). \(Q\) \(L\) \(R\)代表询问你从第\(L\) ...

  6. P1903 [国家集训队]数颜色 / 维护队列 带修改的莫队

    \(\color{#0066ff}{ 题目描述 }\) 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支 ...

  7. P1903 [国家集训队]数颜色 / 维护队列 带修改莫队

    题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2 ...

  8. P1903 [国家集训队]数颜色 / 维护队列

    思路 带修莫队的板子 带修莫队只需要多维护一个时间的指针即可,记录一下每个询问在第几次修改之后,再回退或者前进几个修改操作 排序的时候如果a.l和b.l在一个块里,就看r,如果a.r和b.r在一个块里 ...

  9. P1903 [国家集训队]数颜色 / 维护队列(带修莫队)

    题目描述: 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. ...

随机推荐

  1. vultr 购买vps

    基本安装转自:https://github.com/uxh/shadowsocks_bash/wiki/Vultr%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B 连接 Vul ...

  2. java基础类型数据与String类包装类之间的转换与理解

    数据类型转换一般分为三种: 在java中整型,实型,字符型视为简单数据类型,这些数据类型由低到高分别为:(byte,short,char--int-long-float-double) 简单数据类型之 ...

  3. flask框架路由系统

    flask框架的url系统: flask框架的路由系统: flask框架的路由系统使用实例化的route方法来指定: 如: @app.route('/') route函数装饰器可以把一个函数绑定到对应 ...

  4. Service官方教程(1)Started与Bound的区别、要实现的函数、声明service

    Services 简介和分类 A Service is an application component that can perform long-running operations in the ...

  5. TestNG基本注解(一)

    TestNG基本注解   注解 描述 @BeforeSuite 注解的方法将只运行一次,运行所有测试前此套件中. @AfterSuite 注解的方法将只运行一次此套件中的所有测试都运行之后. @Bef ...

  6. 关于如何读取XML文件的一个简单方法

    在平时开发系统功能的时候,我们经常会碰到一些需求需要经常性的发生变化,比如 系统版本.更新日志 等等.这个时候用一个XML文件来替代数据库,就会变的简便很多. 前段时候我也正好需要改个需求,是关于客户 ...

  7. window.form增删改查

    效果展示: 查询: 可以查询姓名:民族:姓名+民族:都是空的查询全部 取值取得是姓名: 删除: 修改: 先选中查询之后修改: 添加: 代码部分: 第一张表: 第二张表:主表,民族代码加名称 natio ...

  8. AJPFX总结final、finally、finallize的区别

    final.finally.finallize有何区别?    final表示一个修饰符,如果用它来修饰一个类,则该类是不能继承的:如果用它来修饰一个变量,则该变量一旦赋值之后就不能再修改:如果用它来 ...

  9. Spark学习之基于MLlib的机器学习

    Spark学习之基于MLlib的机器学习 1. 机器学习算法尝试根据训练数据(training data)使得表示算法行为的数学目标最大化,并以此来进行预测或作出决定. 2. MLlib完成文本分类任 ...

  10. MVC中使用MVCPager简单分页

    一.建立数据库以及建立MVC项目 自己随便建立一个数据库,并且添加数据.我建立的数据库如下. 二.建立LINQ to SQL映射. 然后一步步点确定 三.编写代码 在Controllers中建立控制器 ...