题目
n(n<=1e5)个数,第i个整数ai范围[1,n]
q(q<=1e5)个询问,每次给出Li,Ri,询问[1,Li]∪[Ri,n]中出现的数的种类
思路来源
凡神
后续
https://ac.nowcoder.com/acm/contest/1084/B 原题警告1
https://codeforces.com/contest/1288/problem/E 原题警告2
按右端点增序,贪心地越右越好,原理一致的,双指针写法
在线处理2*n个位置,把上个位置删掉,把当前位置加回来,只用last一个数组
是常数最小的写法,也更简洁,根本不用处理head数组和next数组
题解
[1,l]和[r,n],先把数组复制一遍,
也就是[r,l+n]的答案了,排序后成为新的[L,R]后处理询问
对于L增序询问,初始L=1,
贪心地选择,选择值出现的第一个位置,越左越好
每删掉一个值的第一个位置(L大于这个位置),
就把这个值的下一个位置加到树状数组里
树状数组里对答案产生贡献的,始终是每个值出现的第一个位置
不在区间询问里的,自然作差就没了
所以需要倒序预处理,每个值出现的首位置和下一个出现的位置
代码1(自己写的)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,q,a[maxn*2];
int tree[maxn*2];
int head[maxn],nex[2*maxn];
int l,r;
bool vis[maxn];
//head[a[i]]:a[i]这个值第一次出现的位置
//next[i]:与i位置相同的值下一次出现的位置
int ans[maxn];
struct node
{
int l,r,id;
node(){}
node(int L,int R,int i):l(L),r(R),id(i){}
}e[maxn];
bool operator<(node a,node b)
{
return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
void add(int x,int v)
{
for(int i=x;i<=n;i+=i&-i)
tree[i]+=v;
}
int sum(int x)
{
int ans=0;
for(int i=x;i>0;i-=i&-i)
ans+=tree[i];
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&q))
{
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
head[i]=2*n+1;
vis[i]=0;
}
for(int i=1;i<=q;++i)
{
scanf("%d%d",&l,&r);
e[i]=node(r,l+n,i);
}
n*=2;
for(int i=n;i>=1;--i)
{
nex[i]=head[a[i]];
head[a[i]]=i;
tree[i]=0;
}
sort(e+1,e+q+1);
int L=1;
for(int i=1;i<=n;++i)
if(!vis[a[i]])
{
vis[a[i]]=1;
add(i,1);
}
for(int i=1;i<=q;++i)
{
while(L<e[i].l)
{
if(nex[L]<=n)add(nex[L],1);
L++;
}
ans[e[i].id]=sum(e[i].r)-sum(e[i].l-1);
}
for(int i=1;i<=q;++i)
printf("%d\n",ans[i]);
}
return 0;
}
代码2(学习)
#include <algorithm>
#include <cstdio>
using namespace std ;
typedef long long LL ;
const int N=5e5+10 ;
struct Query
{
int l , r , id ;
bool operator < ( const Query &q ) const { return r<q.r; }
};
Query q[N] ;
int a[N] , lst[N] ;
LL ans[N] ;
int n , m ;
LL C[N] ;
inline void update ( int x , LL d ) { while ( x<=n ) C[x]+=d,x+=x&-x; }
inline LL query ( int x ) { LL res=0; while ( x>0 ) res+=C[x],x-=x&-x; return res; }
int main ()
{
int i , j ;
while(~scanf("%d%d",&n,&m)){
for ( i=1 ; i<=n ; i++ ) scanf("%d",a+i),a[n+i]=a[i];
for ( i=1 ; i<=m ; i++ ) scanf("%d%d",&q[i].l,&q[i].r),q[i].l+=n,swap(q[i].l,q[i].r),q[i].id=i;
sort(q+1,q+m+1);
n*=2;
for ( i=j=1 ; i<=n ; i++ )
{
if ( lst[a[i]] ) update(lst[a[i]],-1);
lst[a[i]]=i,update(i,1);
while ( q[j].r==i && j<=m ) ans[q[j].id]=query(i)-query(q[j].l-1),j++;
}
for ( i=1 ; i<=m ; i++ ) printf("%lld\n",ans[i]);
for ( i=1 ; i<=n ; i++ ) C[i]=lst[a[i]]=0;
}
return 0 ;
}