Difference NOIp2013-Training Series #1

本文介绍了一种高效解决数列中特定区间内元素互异性问题的算法,通过构建辅助数组和使用树状数组优化查询过程,实现快速判断指定区间内元素是否全不相同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

数列A1,A2, ...,AN,Q 个询问(Li;Ri),ALi,ALi+1,...,ARi 是否互不相同

Input

第1 行,2 个整数N;Q, 
第2 行,N 个整数A1,A2,...,AN 
Q 行,每行2 个整数Li;Ri

Output

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

Sample Input

4 2
1 2 3 2
1 3
2 4

Sample Output

Yes
No

Hint

对于50% 的数据,N,Q<=10^3 
对于100% 的数据,1<=N,Q<=10^5; 1<=Ai<=N; 1<=Li<=Ri<=N


【分析】(感谢nodgd同学提供)

     1.对于每个位置的数字v[i],next[i]记录右边第一个和自己相同的数字的位置下标, last[x]记录最左边一次出现数字x的位置下标。求出这两个数组的方法就和存边一模一样。
     2.把每次提问双关键字排序。
     3.把所有的last[x]标上1,同时用树状数组维护这一些"1"的前缀和
     4.每次提问区间l到r,先把l左边的所有"1"都转移到它的next,也就是让l的左边为0,然后树状数组求出位置下标1到r这一段的前缀和sum,即这一段有多少个"1"。如果sum==r-l+1就说明这一段满满的都是"1",那么这段区间的ans是"Yes";否则"No"。
     5.把所有区间恢复到原来的顺序,依次输出答案

(注:其实这道题跟HZOI的赏花那道题很类似,,传送门:http://blog.youkuaiyun.com/greatwjj/article/details/12288251


【代码】

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define _lowbit(x) (x&(-x))
using namespace std;
const int MAXN=100005;
struct node{int x,y,rank;}Query[MAXN];
int N,Q,v[MAXN],c[MAXN],next[MAXN],last[MAXN],maxv;
bool ans[MAXN];
void _in(int &x)
{
	char t=getchar();
	while(t<'0'||'9'<t) t=getchar();
	for(x=0;'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _qst_xy(int l,int r)
{
	int i=l,j=r,mx=Query[(i+j)>>1].x,my=Query[(i+j)>>1].y;
	while(i<=j)
	{
		while(Query[i].x<mx||(Query[i].x==mx&&Query[i].y<my)) i++;
		while(Query[j].x>mx||(Query[j].x==mx&&Query[j].y>my)) j--;
		if(i<=j)
		{
			swap(Query[i],Query[j]);
			i++;j--;
		}
	}
	if(l<j) _qst_xy(l,j);
	if(i<r) _qst_xy(i,r);
}
void _init()
{
	_in(N);_in(Q);
	for(int i=1;i<=N;i++)
	{
		_in(v[i]);
		maxv=max(maxv,v[i]);
	}
	for(int i=1;i<=Q;i++)
	{
		_in(Query[i].x);
		_in(Query[i].y);
		Query[i].rank=i;
	}
	_qst_xy(1,Q);
}
void _solve()
{
	for(int i=N;i;i--)
	{
		next[i]=last[v[i]];
		last[v[i]]=i;
	}
	for(int i=1;i<=maxv;i++)
		if(last[i]!=0)
			for(int j=last[i];j<=N;j+=_lowbit(j))
				c[j]++;
	for(int i=1;i<=Q;i++)
	{
		for(int k=max(1,Query[i-1].x);k<Query[i].x;k++)
		{
			for(int j=k;j<=N;j+=_lowbit(j))
				c[j]--;
			if(next[k]!=0)
				for(int j=next[k];j<=N;j+=_lowbit(j))
					c[j]++;
		}
		int temp=0;
		for(int j=Query[i].x-1;j;j-=_lowbit(j))
			temp-=c[j];
		for(int j=Query[i].y;j;j-=_lowbit(j))
			temp+=c[j];
		ans[Query[i].rank]=(temp==(Query[i].y+1-Query[i].x))?true:false;
	}
	for(int i=1;i<=Q;i++)
		printf((ans[i])?"Yes\n":"No\n");
}
int main()
{
	_init();
	_solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值