1. 程式人生 > >BZOJ2038: [2009國家集訓隊]小Z的襪子(hose) 莫隊演算法 莫隊演算法講解及時間複雜度證明

BZOJ2038: [2009國家集訓隊]小Z的襪子(hose) 莫隊演算法 莫隊演算法講解及時間複雜度證明

2038: [2009國家集訓隊]小Z的襪子(hose)

Time Limit: 20 Sec  Memory Limit: 259 MB Submit: 7088  Solved: 3258

Description

作為一個生活散漫的人,小Z每天早上都要耗費很久從一堆五顏六色的襪子中找出一雙來穿。終於有一天,小Z再也無法忍受這惱人的找襪子過程,於是他決定聽天由命……
具體來說,小Z把這N只襪子從1N編號,然後從編號LR(L 儘管小Z並不在意兩隻襪子是不是完整的一雙,甚至不在意兩隻襪子是否一左一右,他卻很在意襪子的顏色,畢竟穿兩隻不同色的襪子會很尷尬。你的任務便是告訴小Z,他有多大的概率抽到兩隻顏色相同的襪子。當然,小Z

希望這個概率儘量高,所以他可能會詢問多個(L,R)以方便自己選擇。

Input

輸入檔案第一行包含兩個正整數N和M。N為襪子的數量,M為小Z所提的詢問的數量。接下來一行包含N個正整數Ci,其中Ci表示第i只襪子的顏色,相同的顏色用相同的數字表示。再接下來M行,每行兩個正整數L,R表示一個詢問。

Output

包含M行,對於每個詢問在一行中輸出分數A/B表示從該詢問的區間[L,R]中隨機抽出兩隻襪子顏色相同的概率。若該概率為0則輸出0/1,否則輸出的A/B必須為最簡分數。(詳見樣例)

Sample Input

6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6

Sample Output

2/5
0/1
1/1
4/15
【樣例解釋】
詢問1:共C(5,2)=10種可能,其中抽出兩個2有1種可能,抽出兩個3有3種可能,概率為(1+3)/10=4/10=2/5。
詢問2:共C(3,2)=3種可能,無法抽到顏色相同的襪子,概率為0/3=0/1。
詢問3:共C(3,2)=3種可能,均為抽出兩個3,概率為3/3=1/1。
注:上述C(a, b)表示組合數,組合數C(a, b)等價於在a個不同的物品中選取b個的選取方案數。
【資料規模和約定】
30%的資料中 N,M ≤ 5000;
60%的資料中 N,M ≤ 25000;
100%的資料中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

莫隊演算法:

將1~n分塊
將詢問離線處理,對於左端點在同一塊中的按照右端點的大小排序,右端點較小的在前;
對於左端點不在同一塊中的,按照左端點所在塊大小排序,左端點較小的在前面;
這樣排序之後,我們將排序後的區間從前往後計算,詢問區間之間轉移時只需要新增進來新的點和刪除多餘的點

莫隊時間複雜度證明(最好有一些莫隊的基礎):

0.首先我們知道在轉移一個單位距離的時候時間複雜度是O(1)

1:對於左端點在同一塊中的詢問:
右端點轉移的時間複雜度是O(n),一共有√n塊,所以右端點轉移的時間複雜度是O(n√n)
左端點每次轉移最差情況是O(√n),左端點在塊內會轉移n次,左端點轉移的時間複雜度是O(n√n);

2:對於左端點跨越塊的情況:
會跨區間O(√n)次;
左端點的時間複雜度是O(√n*√n)=O(n)可以忽略不計
右端點因為在跨越塊時是無序的,右端點跨越塊轉移一次最差可能是O(n),可能跨越塊轉移√n次,所以時間複雜度是O(n√n)

所以總的來說莫隊的時間複雜度是O(n√n);

題解:用一點兒數學知識來轉移就可以了,記得約分
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 50010
#define ll long long
ll n,m,ans;
ll c[N],pos[N],s[N];
struct pp
{ll l,r,num;ll son,mom;} a[N];
ll gcd(ll x,ll y)
{
	if(y==0) return x;
	return gcd(y,x%y);
}
bool cmp(pp u,pp v)
{
	if(pos[u.l]==pos[v.l]) return u.r<v.r;
    return u.l<v.l;
}
bool cmp1(pp u,pp v)
{return u.num<v.num;}
void init()
{
	for(ll i=1;i<=n;i++) scanf("%lld",&c[i]);
	ll len=sqrt(n);
	for(ll i=1;i<=n;i++) pos[i]=(i-1)/len+1;
	for(ll i=1;i<=m;i++)
	{
		scanf("%lld%lld",&a[i].l,&a[i].r);
		a[i].num=i;
	}
	sort(a+1,a+m+1,cmp);
}
void modify(ll p,ll add)
{
	ans-=s[c[p]]*s[c[p]];
	s[c[p]]+=add;
	ans+=s[c[p]]*s[c[p]];
}
void modui()
{
	ll l=1,r=0;
	for(ll i=1;i<=m;i++)
	{
		for(;r<a[i].r;r++) modify(r+1,1);
		for(;r>a[i].r;r--) modify(r,-1);
		for(;l<a[i].l;l++) modify(l,-1);
		for(;l>a[i].l;l--) modify(l-1,1);
		if(a[i].l==a[i].r) a[i].son=0,a[i].mom=1;
		else
		{
			a[i].son=ans-(a[i].r-a[i].l+1);
			a[i].mom=(a[i].r-a[i].l+1)*(a[i].r-a[i].l);
			ll k=gcd(a[i].son,a[i].mom);
			a[i].son/=k,a[i].mom/=k;
		}
	}
	sort(a+1,a+m+1,cmp1);
}
int main()
{
	scanf("%lld%lld",&n,&m);
	init();
	modui();
	for(int i=1;i<=m;i++) printf("%lld/%lld\n",a[i].son,a[i].mom);
}