P3901数列求不同洛谷莫队模板题

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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值