先求一个pre数组,表示每个值为a[ i ]的数上一次出现在哪里,如果这个pre[i]在所求区间内,则不应计入数字种类的总数,否则计入。
分块以后把每个块内部的pre数组排序,二分在块内求pre值在不在区间内的数量,再把每个块的总和累计就是所求区间内不同的数的种类,复杂度O(n*sqrt(n)*log( sqrt(n) ) )左右吧。。。
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#define maxn 120000
using namespace std;
int n,m;
int d[maxn],top;
int a[maxn];
int pree[maxn];
int nextt[maxn];
int now[maxn];
int pos[maxn];
int put[maxn];
int block[maxn],l[maxn],r[maxn],num;
int tot;
int List[maxn],sorted[maxn];
void build(){
int num=sqrt(m);
tot=0;
for (int i=1;i<=m;i+=num)
{
++tot;
l[ tot ]=i;
r[ tot ]=i+num-1;
}
r[tot]=m;
for (int i=1;i<=m;i++)
block[i]=(i-1)/num+1;
for (int i=1;i<=m;i++)
sorted[i]=List[i]=pree[i];
for (int i=1;i<=tot;i++)
sort(sorted+l[i],sorted+r[i]+1);
}
int doit(int l,int r,int lim){
int st=l;
while (r-l>1)
{
int mid=(r+l)/2;
if ( sorted[mid]<lim)
l=mid;
else
r=mid;
}
if ( sorted[ r ] < lim )
return r-st+1;
if ( sorted[ l ] < lim )
return l-st+1;
return 0;
}
int ask(int x,int y){
if (block[x]==block[y])
{
int tot=0;
for (int i=x;i<=y;i++)
if (List[i]<x)
tot++;
return tot;
}
int tot=0;
for (int i=x;i<=r[ block[ x ] ] ;i++)
if (List [i]<x)
tot++;
for (int i=l[ block[ y ] ];i <=y; i++)
if (List [i]<x)
tot++;
for (int i=block[ x ]+1;i< block[y];i++)
tot+=doit(l[i],r[i],x);
return tot;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d",&a[i]);
for (int i=1;i<=m;i++)
if (now[ a[i] ]==0)
{
now[ a[i] ]=i;
d[++top]=a[ i ];
pos[ a[ i ] ]=top;
}
else
{
pree[ i ]=now[ a[i] ];
nextt [ now[ a[i] ] ]= i;
now[ a[i] ]=i;
}
for (int i=1;i<=top;i++)
printf("%d ",d[i]);
for (int i=1;i<=n;i++)
if (now[i]==0)
printf("%d ",i);
build();
for (int i=1;i<=m;i++)
if (nextt[i]==0)
printf("%d\n",n-1);
else
printf("%d\n",ask(i,nextt[i]-1)-1);
return 0;
}