P3901 数列找不同 (两种做法 树状数组 和 dp记录其左端点的 最大 值 )

题目描述

现有数列 1,2,…,A1​,A2​,…,AN​,Q 个询问(Li​,Ri​),询问 ALi​​,ALi​+1​,…,ARi​​ 是否互不相同。

输入格式

第一行,两个整数N,Q。
第二行,N 个整数A1​,A2​,…,AN​。
接下来 Q 行,每行两个整数 Li​,Ri​。

输出格式

对每个询问输出一行,Yes 或 No

输入输出样例

输入 #1复制

4 2
1 2 3 2
1 3
2 4

输出 #1复制

Yes
No

说明/提示

对于 50% 的数据,N,Q≤103。
对于 100% 的数据,1≤N,Q≤105,1≤Ai​≤N,1≤Li​≤Ri​≤N。

解析:

思路一:

我们求出以 r为右端点 没有重复的区间 。

代码如下:

// 线段树+离线处理查询
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int M = 1e5+5,L = 20;
int st[M],last[M];
int n,m;
int main()
{
	cin >>n >>m;
	for(int i = 1;i <= n;i++)
	{
		int x;
		scanf("%d",&x);
		st[i] = max(st[i-1],last[x]+1); //不重复的最大左端点
		last[x] = i; 
	}
	for(int i = 1;i <= m;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		if(st[r] <= l){
			cout<<"Yes\n";
		}else{
			cout <<"No\n";
		}
	}
	return 0;
}

时间复杂度为:O(n)

思路二:P1972 [SDOI2009] HH的项链(线段树+离线做法+排序)-CSDN博客

和上面的题一样。我们可以用树状数组进行操作。树状数组是求区间的和。

判断区间的和 与 区间的大小一样,则没有重复的数组。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node//树状数组
{
	int data[101000];
	int num;
	void updata(int pos,int value)
	{
		while(pos<=num&&pos)
		{
			data[pos]+=value;
			pos+=(pos&(-pos));
		}
		return ;
	}
	int sum(int pos)
	{
		int res=0;
		while(pos)
		{
			res+=data[pos];
			pos-=(pos&(-pos));
		}
		return res;
	}
	int check(int l,int r)
	{
		return sum(r)-sum(l-1);
	}
};
node bit;
struct Query
{
	int l;
	int r;
	int num;
}q[101000];//询问
bool compare(const Query &a,const Query &b)
{
	return a.r<b.r;
}
int data[101000];//原数组
int ans[101000];
int last[101000];//最近一次出现的位置
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	bit.num=n;
	for(int i=1;i<=n;i++)
		scanf("%d",&data[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].num=i;//离线处理,是第几个询问。
	}
	sort(q+1,q+1+m,compare);//排序
	int Q=1;
	for(int i=1;i<=n;i++)
	{
		bit.updata(last[data[i]],-1);//撤销上一个记录
		bit.updata(i,1);//更新
		last[data[i]]=i;//保存
		while(q[Q].r==i&&Q<=m)//判断当前右端点是否和某个重合
		{
			if(bit.check(q[Q].l,q[Q].r)==(q[Q].r-q[Q].l+1))//因为是离线算法(fa♂),所以要保存答案
				ans[q[Q].num]=1;
			else
				ans[q[Q].num]=0;
			Q+=1;//写成while保险
		}
	}
	for(int i=1;i<=m;i++)
	{
		if(ans[i]==1)
			printf("Yes\n");
		else
			printf("No\n");
	}
	return 0;
}

时间复杂度为:O(n*logn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值