Light OJ 1188 Fast Queries(離線樹狀數組||莫隊算法)

題目鏈接:

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]);
	}
}

發佈了235 篇原創文章 · 獲贊 4 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章