P3901数列求不同 洛谷莫队模板题
首先了解莫队
学习资料
莫队思路介绍
算法核心 指针移动
如上图,假如我们要求Q2的区间和,并且我们知道了Q1的区间和
1.我们可以把Q1的右指针右移,沿路加上经过数值的值,这样我们就算出了区间[1,15]的区间和
2.然后我们将Q1的左指针右移,沿路减去经过数值的值,这样我们就算出了区间[6,15]的区间和
莫队核心
分块silu:我们将长度为n的序列分成O(n−−√)块,从1至n−−√编号。
然后标记每一个点所属的块block[i] (表示i点属于块的编号)
然后我们对询问排序:
先按左端点所在的块编号为第一关键字排序
如果相同,则按右端点的位置排序
然后按排序后的顺序处理每一个询问,求得答案
再通过一次排序复原原来的顺序。
两个思路的代码如下:
题目: P3901 数列找不同 模板题
https://www.luogu.com.cn/problem/P3901
莫队 多个区间查询操作
莫队算法核心 指针移动 复杂度O(n*n)
void add(int x){if(vis[v[x]]==1)ans++;vis[v[x]]++;} //加入操作
void remove(int x){vis[v[x]]--;if(vis[v[x]]==1)ans--;}//删除操作
l=1;r=0;
for(int i=1;i<=m;i++){
while(l>a[i].l)add(--l);//左指针往左移加入
while(l<a[i].l)remove(l++);//左指针往右移删除
while(r<a[i].r)add(++r);//右指针往右移加入
while(r>a[i].r)remove(r--);//右指针往左移删除
if(ans==0)a[i].ans=0; //Yes
else a[i].ans=1; //No
}
莫队重中之重 分块询问顺序 复杂度O(n*n)-->O(n*sqrt(n))
bool cmp1(node a,node b){return a.id<b.id;} //复原原来顺序
bool cmp(node a,node b){ //询问排序
if(blo[a.l]!=blo[b.l])return a.l<b.l; //先按左端点所在块为第一关键字排序
else return a.r<b.r; //相同则按右端点排序
}
……
block=sqrt(n); //块大小
for(int i=1;i<=n;i++)blo[i]=(i-1)/block+1; //划分块 0-n
for(int i=1;i<=m;i++){ //将测试数据输入进行排序
scanf("%lld%lld",&a[i].l,&a[i].r);
a[i].id=i; //标记原来编号
}
sort(a+1,a+m+1,cmp); //对询问排序 O(n*logn)
……//上面的莫队核心指针移动
sort(a+1,a+m+1,cmp1); //复原顺序
题目代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct node{
int l,r,id,res;
}a[N];
int cnt[N],vis[N],blo[N],bl;
bool cmp(node a,node b){
return (a.l / bl) == (b.l / bl) ? a.r < b.r : a.l < b.l;
/*写成这样复杂度增加10倍超时
if(a.l==b.l) return a.r<b.r;
return a.l<b.l;
*/
}
bool cmp1(node a,node b){
return a.id<b.id;
}
int ans=0;
void add(int x){
if(vis[cnt[x]]==1) ans++;
vis[cnt[x]]++;
}
void remove(int x){
vis[cnt[x]]--;
if(vis[cnt[x]]==1) ans--;
}
int main(){
memset(vis,0,sizeof(vis));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&cnt[i]);
for(int i=1;i<=m;i++){//输入
a[i].id=i;
scanf("%d%d",&a[i].l,&a[i].r);
}
bl=sqrt(n);//分块
sort(a+1,a+m+1,cmp);//对数据按照指针进行排序
int L=1,R=0;
for(int i=1;i<=m;i++){
while(L<a[i].l) remove(L++);
while(L>a[i].l) add(--L);
while(R<a[i].r) add(++R);
while(R>a[i].r) remove(R--);
a[i].res=ans;
}
sort(a+1,a+m+1,cmp1);//以id复原结构体数组
for(int i=1;i<=m;i++){
if(a[i].res) printf("No\n");
else printf("Yes\n");
}
return 0;
}