题目描述
现有数列 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)