[带修莫队] Bzoj 2120 数颜色

Description

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?

Input

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

Output

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

Sample Input

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

Sample Output

4
4
3
4

HINT

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

题解

  • 可修改的莫队,怎么做呢?
  • 普通的莫队做法肯定会爆炸
  • 我们可以先引入一个“修改时间”,表示当前询问是发生在前Time个修改后的。
  • 也就是说我们在做莫队的时候除了原先的两个l,r指针,还引入时间指针,可以通过时间倒流或时光推移来保证正确性
  • 按照原先的sort函数来说,使得每个询问最坏情况下时间指针可以移动n次,总共就n^2次,极其不优秀,那么下面我们就要用三关键字排序来做
  • 时间复杂度分析:(num=sqrt(n))
  • ①对于l指针,依旧是O(num*n)
  • ②对于r指针,也依旧是O(n*n/num)
  • ③对于t指针(时间指针):
  • 我们要寻找有多少个单调段(一个单调段下来最多移动n次) 在排序函数里,当且仅当两个询问l在同块,r也在同块时,才会对可怜的t进行排序。
  • 对于每一个l的块,里面r最坏情况下占据了所有的块
  • 所以最坏情况下:有n/num个l的块,每个l的块中会有n/num个r的块,此时,在一个r块里,就会出现有序的t。
  • 所以t的单调段个数为:(n/unit)^2。
  • 每个单调段最多移动n次。 最后所以时间指针的时间复杂度为 O((n/num)^2*n)
  • 三个指针汇总:O(num*n+n^2/num+(n/num)^2*n)。当num=2/3n时,时间最优秀,为O(n^3/5)

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cmath>
 5 #define N 10010
 6 using namespace std;
 7 struct Query{int l,r,t,d;}q[N];
 8 struct Change{int w,l,r;}p[N];
 9 int n,m,ans,l=1,r,k,a[N],col[N*100],Time,numq,now[N],num,bel[N],Ans[N];
10 bool cmp(Query a,Query b) { return bel[a.l]==bel[b.l]?(bel[a.r]==bel[b.r]?a.t<b.t:a.r<b.r):a.l<b.l; }
11 void change(int x,int y) { col[x]+=y; if (y>0) ans+=col[x]==1; if (y<0) ans-=col[x]==0; }
12 void pd(int x,int y) { if (l<=x&&x<=r) change(y,1),change(a[x],-1); a[x]=y; }
13 int main()
14 {
15     scanf("%d%d",&n,&m),num=pow(n,0.66666);
16     for (int i=1;i<=n;i++) scanf("%d",&a[i]),now[i]=a[i],bel[i]=i/num+1;
17     for (int i=1,x,y;i<=m;i++)
18     {
19         char ch;
20         scanf(" %c %d%d",&ch,&x,&y);
21         if (ch==‘Q‘) q[++numq]=(Query){x,y,Time,numq};
22         if (ch==‘R‘) p[++Time]=(Change){x,y,now[x]},now[x]=y;
23     }
24     sort(q+1,q+numq+1,cmp);
25     for (int i=1;i<=numq;i++)
26     {
27         while (k<q[i].t) pd(p[k+1].w,p[k+1].l),k++;
28         while (k>q[i].t) pd(p[k].w,p[k].r),k--;
29         while (l<q[i].l) change(a[l],-1),l++;
30         while (l>q[i].l) change(a[l-1],1),l--;
31         while (r<q[i].r) change(a[r+1],1),r++;
32         while (r>q[i].r) change(a[r],-1),r--;
33         Ans[q[i].d]=ans;
34     }
35     for (int i=1;i<=numq;i++) printf("%d\n",Ans[i]);
36 }

原文地址:https://www.cnblogs.com/Comfortable/p/10364519.html

时间: 2024-10-10 14:49:30

[带修莫队] Bzoj 2120 数颜色的相关文章

【带修莫队】bzoj2120 数颜色

块大小为n1/3. 把询问和修改分开. 每次两个询问之间的修改进行暴力转移,如果修改在上一次询问的区间里,就会对当前状态形成影响. 好慢. #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define N 10001 int num[N],n,m,b[N],a[N],enq,enc,anss[N]; struct ASK{

2120: 数颜色(带修莫队)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2120 2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MBSubmit: 10514  Solved: 4398[Submit][Status][Discuss] Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R

数颜色(带修莫队模板)

数颜色(luogu) Description 题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会向你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2. R P Col 把第P支画笔替换为颜色Col. 为了满足墨墨的要求,你知道你需要干什么了吗? 输入格式 第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数. 第2行N个整数,分别代表初始画笔排中第i支画笔的颜色. 第3行到第2+M行

【BZOJ-3052】糖果公园 树上带修莫队算法

3052: [wc2013]糖果公园 Time Limit: 200 Sec  Memory Limit: 512 MBSubmit: 883  Solved: 419[Submit][Status][Discuss] Description Input Output Sample Input Sample Output 84 131 27 84 HINT Source Solution 树上带修莫队 本质还是树上莫队,详情可以转 BZOJ-3757苹果树 但是这里需要修改,就需要一些特殊的地方

bzoj4129 Haruna’s Breakfast 树上带修莫队+分块

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4129 题解 考虑没有修改的序列上的版本应该怎么做: 弱化的题目应该是这样的: 给定一个序列,每次询问区间 \([l, r]\) 中元素的最小没有出现的自然数. 这个弱化的版本可以用离线+线段树二分水掉.但是这个做法显然不太好搬到树上做. 上面的弱化版还有一个莫队做法:可以用莫队维护出来每一个区间的每一个数的出现为次数.把出现过的数通过分块表示出来,于是查询的时候枚举每一个块,寻找第一个不满的

bzoj 2120: 数颜色 线段树套平衡树

/************************************************************** Problem: 2120 User: wangyucheng Language: C++ Result: Time_Limit_Exceed ****************************************************************/ #include<iostream> #include<cstdio> #incl

【带修莫队】【权值分块】bzoj3196 Tyvj 1730 二逼平衡树

这题用了三种算法写: 分块+二分:O(n*sqrt(n*log(n)) 函数式权值分块:O(n*sqrt(n)) 带修莫队+权值分块:O(n5/3) 结果……复杂度越高的实际上跑得越快……最后这个竟然进第一页了…… #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int f,C; inline void R(int &

CF940F Machine Learning(带修莫队)

首先显然应该把数组离散化,然后发现是个带修莫队裸题,但是求mex比较讨厌,怎么办?其实可以这样求:记录每个数出现的次数,以及出现次数的出现次数.至于求mex,直接暴力扫最小的出现次数的出现次数为0的正整数,就一句话,这样看似会超时,实际上是O(√n)的复杂度.为什么?假设存在出现1,2,...,x的出现次数,则Σi(1<=i<=x)<=n,即x*(x+1)<=2*n,所以x至多是√n级别.很多人再把出现次数分块,根本没必要.然后考虑把数组分块的块大小,每次移动左指针,为O(n*块大

莫队 + 带修莫队

莫队其实就是一个带优化的暴力,通过将区间询问按一定规则进行排序,从而优化过程,求出答案. 举一例子:(例子不具备权威性,只是让读者了解莫队是干啥的) /* 输入四个区间 1 4 初始条件,L= R = 0, 将R遍历到4 需要走4步 L走一步,共5次 4 8 继承上一次 L 和 R 的值,L从1到4 需要3次,R从4到8,需4次, 总计8次 2 9 同理继承, L回退2次, R前进一次 总计3次 1 2 同理,L回退1次,R回退7次 总计8次 如果直接暴力,计算机将要计算 5+8+3+8=24次