莫队 [洛谷2709] 小B的询问[洛谷1903]【模板】分块/带修改莫队(数颜色)
莫队--------一个优雅的暴力
莫队是一个可以在O(n√n)内求出绝大部分无修改的离线的区间问题的答案(只要问题满足转移是O(1)的)即你已知区间[l,r]的解,能在O(1)的时间内求出[l-1,r][l+1,r][l,r-1][l,r+1]的解。否则时间复杂度为O(kn√n)(k为转移的时间)
以下默认转移是O(1)的
显然,我们如果得知[l,r]的解,我们便可以在O(|l2-l|+|r2-r|)的时间内求出[l2,r2]的解
那么,对于q个询问(假设q与n同数量级),我们如果能找到一个合适的顺序求解这q个询问,便能降低时间复杂度
对于最原始的暴力,我们每次从l遍历到r来求解(l,r为区间查询的左右端点),复杂度O(n^2)
试着找到一个合适的求解顺序,我们把l,r想象成一个平面直角坐标系上的点(l,r),则所有询问间转移的花费就为在沿着这个平面上最小直线斯坦纳树做的花费。
挺简单的是吧?
但是我们有个良好的替代品,分块。
我们把整个数列分成T块(T=√n),然后我们记下每个询问的左端点l所在的块block[i],按block为第一关键字,r为第二关键字从小到大排序,依次求解就能使时间复杂度降至O(n√n)
简单说明一下:
左指针移动最坏情况下是从一块的开始跳到结尾然后再跳回开始......(因为你从这块跳到下一块后就无法跳回来了)时间复杂度O(n√n)
右指针移动最坏情况下是对于每个在不同块内的左指针,右指针都要从头跳到尾(在同一块内的多个左指针,从头跳到尾的过程中肯定也一起处理了)时间复杂度O(n√n)
在有些情况下,如果莫队的删除操作时间复杂度过高,而添加操作的时间复杂度极低,那我们考虑维护询问左端点所在的块的右端点到询问右端点之间的答案(左右端点所在块相同的暴力for一遍处理) (左端点到左端点所在的块的右端点的答案暴力计算)具体参见[BZOJ4241]历史研究(回滚莫队)
此外,莫队还有一个很好的Debug的方法,就用样例,不断调整分块大小T,看看答案是否不变。
[洛谷2709] 小B的询问
题目描述
小B有一个序列,包含N个1~K之间的整数。他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。
输入输出格式
输入格式:
第一行,三个整数N、M、K。
第二行,N个整数,表示小B的序列。
接下来的M行,每行两个整数L、R。
输出格式:
M行,每行一个整数,其中第i行的整数表示第i个询问的答案。
输入输出样例
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6
6
9
5
2
说明
对于全部的数据,1<=N、M、K<=50000
我们这道题为例讲讲莫队的实现,首先排序,注意排序是把询问排序,不是叫你把原数组排序
然后初始化双指针l=1,r=0,表示我们已经得知[l,r]的答案
我们用cnt数组记录每个数的出现次数(如果K<=10^9,那么需要离散化)
然后莫队最难的地方来了,转移。即用区间[l,r]求出区间[l-1,r][l+1,r][l,r-1][l,r+1]的值
由完全平方公式可知,区间中多出一个数字i,对答案的贡献为i之前的出现次数(即cnt[i])*2+1,区间中少一个数字i,对答案的贡献为-cnt[i]*2+1
语序!!!显然,我们是先计算答案后cnt++(--),但是l和r什么时候++(--)
对于使区间变大的操作(即l--,r++)我们应先使l--,r++后再进行后续的操作(因为这样我们操作的数才是我们真正添加进来的数)
对于使区间变小的操作(即l++,r--)我们应先进行操作后使l++,r--(因为这样我们操作的数才是我们真正删除的数)

绿色是要加入(删除的数),黄色是此时的r指针,自己对着理解一遍吧
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct xxx{
int l,r,id,block;
}q[];
int cnt[],a[];long long ans[];
bool cmp(xxx a,xxx b){if(a.block!=b.block)return a.block<b.block;return a.r<b.r;}
int main()
{
int n,m,k;scanf("%d%d%d",&n,&m,&k);int T=(int)sqrt((double)n);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
for(int i=;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;q[i].block=(q[i].l+)/T;
}
sort(q+,q+m+,cmp);
int l=,r=;long long sum=;
for(int i=;i<=m;i++)
{
while(r<q[i].r){sum=sum+*cnt[a[++r]]+;cnt[a[r]]++;}
while(r>q[i].r){sum=sum-*cnt[a[r]]+;cnt[a[r--]]--;}
while(l<q[i].l){sum=sum-*cnt[a[l]]+;cnt[a[l++]]--;}
while(l>q[i].l){sum=sum+*cnt[a[--l]]+;cnt[a[l]]++;}
ans[q[i].id]=sum;
}
for(int i=;i<=m;i++)printf("%lld\n",ans[i]);
return ;
}
那如果区间操作不只有查询,还有修改呢?(以下3√n表示3次根号n)(注意,莫队的修改只能单点修改)
以下记题目给出的原始数组为a数组
我们把所有操作按发生时间假想成一个时间轴,绿色表示修改操作,红色表示查询操作

我们可以发现,对于每个查询操作,它只和在它之前发生的修改操作有关
我们考虑用x[i]表示第i个查询操作前进行了多少次修改,然后再用一个变量now记录当前进行了多少次修改,这样我们就能计算出要还原(新增)几次修改。
对于修改,我们要记录它的几个要素,pre:修改前的值(便于还原),val:修改后的值(便于修改),no:修改哪个数。
然后像暴力移动l,r指针一样,我们也暴力移动修改指针now
我们把整个数列分成T块(T=3√n),然后我们记下每个询问的左端点l所在的块blockl[i],每个询问的右端点r所在的块blockr[i],按blockl为第一关键字,blockr为第二关键字,x为第三关键字从小到大排序,依次求解就能使时间复杂度降至O(n的5/3次方)
对于每一个修改,分在当前[l,r]区间内(不是当前循环到的query的区间)和区间外考虑,区间外就直接修改a数组的值,区间内要考虑cnt的变化以及sum (当前区间内不同种类的数的个数)的加减(和l,r指针移动一样考虑)
简要说明一下带修改莫队的时间复杂度。
左右指针如上面分析,时间复杂度O(n的5/3次方)
修改指针最坏情况下对于任意两个块lblock(l在的那个块)与rblock(r在的那个块),修改指针最坏从1跑到n,而这样的情况共有3√n ^2种,所以时间复杂度O(n的5/3次方)
注意点:在读入过程中,a数组遇到修改操作也是要修改的,否则接下来的修改操作如果还有修改这一个点的话,一个pre就会记录成最初始的原数组a[]了,最后在所有操作结束后,当前被你修改的乱七八糟的数组还是要还原为a数组。
下面程序中的T是块内元素个数,不是有多少块(3√n^2)
[洛谷1903]【模板】分块/带修改莫队(数颜色)
题目描述
墨墨购买了一套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支画笔中共有几种不同颜色的画笔。
输入输出样例
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
4
4
3
4
说明
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct xxx{
int l,r,blockl,blockr,id,t;
}data[];
struct xxx2{
int pre,val,no;
}d[];
int a[],cnt[],ans[];
bool cmp(xxx a,xxx b)
{
if(a.blockl!=b.blockl)return a.blockl<b.blockl;
if(a.blockr!=b.blockr)return a.blockr<b.blockr;
return a.t<b.t;
}
int main()
{
int n,m;scanf("%d%d",&n,&m);int T=(int)pow((double)n,0.66666666666);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
int totq=,totr=;
for(int i=;i<=m;i++)
{
char c[];int l,r;
scanf("%s%d%d",c,&l,&r);
if(c[]=='Q')
{
data[++totq].l=l;data[totq].r=r;data[totq].id=totq;
data[totq].blockl=(l+)/T;data[totq].blockr=(r+)/T;data[totq].t=totr;
}
if(c[]=='R')
{
d[++totr].no=l;d[totr].val=r;d[totr].pre=a[l];a[l]=r;
}
}
for(int i=m;i>=;i--)a[d[i].no]=d[i].pre;
sort(data+,data+totq+,cmp);
int l=,r=,now=,sum=;
for(int i=;i<=totq;i++)
{
while(now<data[i].t)
{
now++;
if(d[now].no>=l&&d[now].no<=r)
{
cnt[d[now].pre]--;
if(cnt[d[now].pre]==)sum--;
cnt[d[now].val]++;
if(cnt[d[now].val]==)sum++;
}
a[d[now].no]=d[now].val;
}
while(now>data[i].t)
{
if(d[now].no>=l&&d[now].no<=r)
{
cnt[d[now].val]--;
if(cnt[d[now].val]==)sum--;
cnt[d[now].pre]++;
if(cnt[d[now].pre]==)sum++;
}
a[d[now].no]=d[now].pre;now--;
}
while(r<data[i].r){cnt[a[++r]]++;if(cnt[a[r]]==)sum++;}
while(r>data[i].r){cnt[a[r]]--;if(cnt[a[r--]]==)sum--;}
while(l<data[i].l){cnt[a[l]]--;if(cnt[a[l++]]==)sum--;}
while(l>data[i].l){cnt[a[--l]]++;if(cnt[a[l]]==)sum++;}
ans[data[i].id]=sum;
}
for(int i=;i<=totq;i++)printf("%d\n",ans[i]);
return ;
}
莫队 [洛谷2709] 小B的询问[洛谷1903]【模板】分块/带修改莫队(数颜色)的更多相关文章
- AC日记——【模板】分块/带修改莫队(数颜色) 洛谷 P1903
[模板]分块/带修改莫队(数颜色) 思路: 带修改莫队: (伏地膜xxy): 代码: #include <bits/stdc++.h> using namespace std; #defi ...
- 【BZOJ】4129: Haruna’s Breakfast 树分块+带修改莫队算法
[题意]给定n个节点的树,每个节点有一个数字ai,m次操作:修改一个节点的数字,或询问一条树链的数字集合的mex值.n,m<=5*10^4,0<=ai<=10^9. [算法]树分块+ ...
- 【BZOJ】3052: [wc2013]糖果公园 树分块+带修改莫队算法
[题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的 ...
- 洛谷 P1903 BZOJ 2120 清橙 A1274【模板】分块/带修改莫队(数颜色)(周奕超)
试题来源 2011中国国家集训队命题答辩 题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔 ...
- P1903 【模板】分块/带修改莫队(数颜色)
题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2 ...
- 洛谷2709 小B的询问(莫队)
题面 题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R] ...
- 洛谷.2709.小B的询问(莫队)
题目链接 /* 数列的最大值保证<=50000(k),可以直接用莫队.否则要离散化 */ #include<cmath> #include<cstdio> #includ ...
- 洛谷 P1903 【模板】分块/带修改莫队(数颜色)
题目描述 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔. 2 ...
- luogu1903 【模板】分块/带修改莫队(数颜色)
莫队算法模板 推荐阅读这篇博客 #include <algorithm> #include <iostream> #include <cstdio> #includ ...
随机推荐
- datetime模块及time模块
pyhton的datetime模块分析(小女子的测试之路):https://www.cnblogs.com/cindy-cindy/p/6720196.html python时间模块小结(time a ...
- sql查询平均下单时间
SQL查询订单平均审核时长 今天在写一个sql,需求是算一个订单在执行状态中的各个节点的时长 比如在订单中,状态0为开始接单,状态3为已经审核,那么现在需要计算每个客服的平均审核时长 像图中所示:这个 ...
- POJ:2367-Cleaning Shifts
传送门:http://poj.org/problem?id=2376 Cleaning Shifts Time Limit: 1000MS Memory Limit: 65536K Total Sub ...
- python基础之继承组合应用、对象序列化和反序列化,选课系统综合示例
继承+组合应用示例 1 class Date: #定义时间类,包含姓名.年.月.日,用于返回生日 2 def __init__(self,name,year,mon,day): 3 self.name ...
- 7,MongoDB 之 Limit 选取 Skip 跳过 Sort 排序
我们已经学过MongoDB的 find() 查询功能了,在关系型数据库中的选取(limit),排序(sort) MongoDB中同样有,而且使用起来更是简单 首先我们看下添加几条Document进来 ...
- P2580 于是他错误的点名开始了(trie)
P2580 于是他错误的点名开始了 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉 ...
- ionic3中关于Ionic ui component使用的一些总结
在我的理解中,IONIC中例如 ion-list ion-item ion-input 其实就是相于一段自定义的指令 ,相对于angular1.x中的东西, 所以,我们在使用中,要特别去注意butt ...
- 开启虚拟机所报的错误:VMware Workstation cannot connect to the virtual machine. Make sure you have rights to run the program, access all directories the program uses, and access all directories for temporary fil
当我们开启虚拟机时出现错误: VMware Workstation cannot connect to the virtual machine. Make sure you have rights t ...
- 资料--JavaScript原型链
JavaScript原型链 原文出处:https://www.cnblogs.com/chengzp/p/prototype.html 目录 创建对象有几种方法 原型.构造函数.实例.原型链 inst ...
- 使用Eclipse把java文件打包成jar
本例仅限于要打包的文件没有使用第三方的jar包 在要打包的包或者文件上右键-Export 这里有一些选项: Export generated class files and resources 表示只 ...