莫队 [洛谷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 ...
随机推荐
- 词向量1.md
词向量 我们以句子分类为例,我们使用深度学习模型对句子进行分类,本质上这个模型的接受的舒服需要是数值型.因为文字是人们抽象出来的一个概念,这个 东西是不能被计算机直接理解的,我们需要人为的将这个文字转 ...
- Hive 文件格式 & Hive操作(外部表、内部表、区、桶、视图、索引、join用法、内置操作符与函数、复合类型、用户自定义函数UDF、查询优化和权限控制)
本博文的主要内容如下: Hive文件存储格式 Hive 操作之表操作:创建外.内部表 Hive操作之表操作:表查询 Hive操作之表操作:数据加载 Hive操作之表操作:插入单表.插入多表 Hive语 ...
- 初见spark-04(高级算子)
今天,这个是spark的高级算子的讲解的最后一个章节,今天我们来介绍几个简单的算子, countByKey val rdd1 = sc.parallelize(List(("a", ...
- 12,DBUtils - Python数据库连接池
创建数据库连接池: import time import pymysql import threading from DBUtils.PooledDB import PooledDB, SharedD ...
- P1297 网线切割
P1297 网线切割 题目描述 Wonderland居民决定举行一届地区性程序设计大赛.仲裁委员会志愿负责这次赛事并且保证会组织一次有史以来最公正的比赛.为此,所有参赛者的电脑和网络中心会以星状网络连 ...
- Docker构建nginx+uwsgi+flask镜像(一)
前言 笔者之前是从事Java方面的工作,自从18年5月左右来到新的公司,接触到Python,被其简单优雅的语法风格深深吸引,同时,新公司也帮助笔者打开Docker世界的大门,让笔者体会到“一次打包,到 ...
- sublimeText3快捷键
Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件Ctrl+Shift+V:粘贴 ...
- 《Cracking the Coding Interview》——第7章:数学和概率论——题目5
2014-03-20 02:20 题目:给定二维平面上两个正方形,用一条直线将俩方块划分成面积相等的两部分. 解法:穿过对称中心的线会将面积等分,所以连接两个中心即可.如果两个中心恰好重合,那么任意穿 ...
- Vbs 测试程序三
一段被写滥了的VBS代码 值得一提的是感兴趣的同学可以把 chr(13) + chr(13) + chr(13) 改为chr(11) + chr(11) + chr(11) 不赘述 on error ...
- Windows下安装jenkins,关闭jenkins,修改jenkins端口号
1.Jenkins安装部署 在官网下载Jenkins: https://jenkins.io/download/thank-you-downloading-windows-installer-stab ...