题意:
给出一个长度为n的序列,m次查询区间[l,r]中出现两次的数的种类数;
n,m<=1000000;
题解:
一开始看出来数据范围。。少看一个0;
于是写了一发莫队,O(n√n)直接TLE掉;
这题正解当然是O(nlogn)的算法咯,和HH的项链那道题的思想确实很相似;
我们记录与第i个数相同的最后一个数下标为pre[i];
将询问离线,按r端点排序,每扫到一个点i就回答r端点为i的所有询问;
扫过i时,我们在树状数组中将pre[i]的值+1,pre[pre[i]]的值-1;
这时,对于一个区间[l,r]来说,直接查询[l,r]的区间和吗是答案咯;
并不需要区间+1什么的操作呢,况且树状数组维护那东西我又不会= =;
时间复杂度O(nlogn),代码复杂度也是很低的;
代码:
#include<cctype>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
#define LEN 1<<16
using namespace std;
struct node
{
int l,r,no;
friend bool operator<(node a,node b)
{
return a.r<b.r;
}
}Q[N];
int a[N],pre[N],last[N],sum[N],ans[N],n;
char getc()
{
static char *S,*T,buf[LEN];
if(S==T)
{
T=(S=buf)+fread(buf,1,LEN,stdin);
if(S==T)
return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0';isdigit(ch=getc());)
D=D*10+ch-'0';
return D;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)
{
if(!x) return ;
while(x<=n)
{
sum[x]+=val;
x+=lowbit(x);
}
}
int query(int x)
{
int ret=0;
while(x)
{
ret+=sum[x];
x-=lowbit(x);
}
return ret;
}
int main()
{
int c,m,i,j,k,l,r;
n=read(),c=read(),m=read();
for(i=1;i<=n;i++)
a[i]=read(),pre[i]=last[a[i]],last[a[i]]=i;
for(i=1;i<=m;i++)
{
Q[i].l=read(),Q[i].r=read();
Q[i].no=i;
}
sort(Q+1,Q+m+1);
for(i=1,j=1;i<=n;i++)
{
update(pre[i],1);
update(pre[pre[i]],-1);
while(j<=m&&Q[j].r==i)
{
ans[Q[j].no]=query(i)-query(Q[j].l-1);
j++;
}
}
for(i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}

本文介绍了解决特定区间查询问题的高效算法,通过记录每个数的相同数上一个位置和离线排序的方式,实现O(nlogn)的时间复杂度。详细解释了算法思想和代码实现,旨在提供一种快速解决类似问题的方法。
446

被折叠的 条评论
为什么被折叠?



