題目鏈接:
http://lightoj.com/volume_showproblem.php?problem=1188
題目大意:
給一串長爲n的序列,然後有m個區間詢問。要求得到詢問區間範圍內不同數的種類。
範圍:
n<=10^5,m<=50000.
思路:
暴力肯定是行不通的了。
對於這類問題,我們自然容易想到樹狀數組。在樹狀數組上存下不同數的種類實現在logn的複雜度內解決問題。
但是要做一些處理,如果我們單純的通過判斷某個數是否第一次出現來存下樹狀數組,就只能夠在整體的區間裏面知道答案,對於中間的區間就不行了。
這裏我們就採用離線處理的方法,將m個詢問以右區間從小到大進行排序,然後通過樹狀數組解決問題。
爲什麼這樣可以呢?我們在進行樹狀數組操作的時候,每次遇到一個新的數,就標記爲1,同時將c數組加1。如果遇到一個已經出現過的數,就將當前數標記爲1,上一個出現數的位置的c數組減1。這樣我們就能夠保證在當前區間情況下,每個不同的數都只被標記了一次。而離線處理以後我能夠保證前面的詢問操作不再需要進行,所以這樣就保證了正確性。
樹狀數組代碼:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
int n,c[100005];
struct node {
int l,r, idx;
}p[100005];
int lowbit(int x)
{
return x&(-x);
}
void update(int x)
{
int ans=0;
while(x<=n)
{
c[x]++;
x+=lowbit(x);
}
}
void update2(int x)
{
while(x<=n)
{
c[x]--;
x+=lowbit(x);
}
}
int query(int x)
{
int ans=0;
while(x)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
bool cmp(node a,node b)
{
return a.r<b.r;
}
int main()
{
int T,i,j,k,q,a[100005],vis[100005],x,ans[100005],pos[100005],icase=0;
scanf("%d",&T);
while(T--)
{
icase++;
memset(pos,0,sizeof(pos));
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
{scanf("%d",&a[i]);
}
for(i=1;i<=q;i++)
{
scanf("%d%d",&p[i].l,&p[i].r);
p[i].idx=i;
}
sort(p+1,p+1+q,cmp);
int x;
vis[a[1]]=1;
update(1);
pos[a[1]]=1;
x=1;
for(i=1;i<=q;i++)
{
for(j=x+1;j<=p[i].r;j++)
{
if(!vis[a[j]]){update(j);pos[a[j]]=j;}
else {
update2(pos[a[j]]);
update(j);
pos[a[j]]=j;
}
vis[a[j]]=1;
}
x=p[i].r;
ans[p[i].idx]=query(p[i].r)-query(p[i].l-1);
}
printf("Case %d:\n",icase);
for(i=1;i<=q;i++)
printf("%d\n",ans[i]);
}
}
方法2:比較裸的莫隊算法。
代碼:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define ll long long
using namespace std;
struct node{
ll l,r,idx;
}p[100005];
ll block,pos[100005];
ll n,m,K,i,j,a[100005],x[100005],ans[100005],icase=0;
bool cmp(node aa,node bb)
{
if(pos[aa.l]==pos[bb.l])return aa.r<bb.r;
return pos[aa.l]<pos[bb.l];
}
int main()
{
ll T;
scanf("%I64d",&T);
while(T--)
{
icase++;
scanf("%lld%lld",&n,&m);
block=ceil(sqrt(n));
// memset(num,0,sizeof(num));
memset(ans,0,sizeof(ans));
memset(x,0,sizeof(x));
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=i/block;
}
for(i=1;i<=m;i++)
{
scanf("%lld%lld",&p[i].l,&p[i].r);
p[i].idx=i;
}
sort(p+1,p+1+m,cmp);
ll L,R;
ll sum=0;
L=1;R=0;
for(i=1;i<=m;i++)
{
// printf("%I64d %I64d\n",p[i].l,p[i].r);
ll idx=p[i].idx;
while(R<p[i].r)
{
R++;
x[a[R]]++;
if(x[a[R]]==1)sum++;
}
while(L>p[i].l){
L--;
x[a[L]]++;
if(x[a[L]]==1)sum++;
}
while(R>p[i].r){
x[a[R]]--;
if(x[a[R]]==0)sum--;
R--;
}
while(L<p[i].l){
x[a[L]]--;
if(x[a[L]]==0)sum--;
L++;
}
// printf("%I64d\n",sum);
ans[idx]=sum;
}
printf("Case %lld:\n",icase);
for(i=1;i<=m;i++)
printf("%lld\n",ans[i]);
}
}