bzoj 2038(莫隊算法)

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

時間限制: 20 Sec  內存限制: 259 MB

題目描述

做爲一個生活散漫的人,小Z天天早上都要耗費好久從一堆五光十色的襪子中找出一雙來穿。終於有一天,小Z再也沒法忍受這惱人的找襪子過程,因而他決定聽天由命……
具體來講,小Z把這N只襪子從1到N編號,而後從編號L到R(L 儘管小Z並不在乎兩隻襪子是否是完整的一雙,甚至不在乎兩隻襪子是否一左一右,他卻很在乎襪子的顏色,畢竟穿兩隻不一樣色的襪子會很尷尬。
你的任務即是告訴小Z,他有多大的機率抽到兩隻顏色相同的襪子。固然,小Z但願這個機率儘可能高,因此他可能會詢問多個(L,R)以方便本身選擇。
c++

輸入

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

輸出

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

樣例輸入

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

樣例輸出

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。
 
——————————————————————————————————————————————————————————
莫隊算法,發明者莫濤。
功能:可以實現對數列上區間的各類查詢,這聽上去和線段樹很像,可是它可以查詢一些不具有加和性的問題,這是線段樹沒法解決的。
應用條件:f(l,r)表示區間[l,r]上查詢結果,它若是在O(1)時間內計算出如下4個表達式,則可使用莫隊算法。
f(l+1,r)、f(l-1,r)f(l,r+1)、f(l,r-1)
思路:莫隊算法真正證實了那句名言:「暴力出奇跡」
一、首先將全部的查詢排序,方法爲:l 所在的塊小的,或 l 所在的塊相同可是r小的靠前排。塊的大小爲n 1/2
二、初始化區間爲空,並設置相應的ans
三、將區間按排好查詢的順序依次向每一次查詢的區間靠近(這就是爲什麼要有上面的應用條件)。並保存答案。
四、依次輸出答案。
複雜度:
n個數m次查詢
排序:m*log(m)
查詢:第次移動爲1,l 的移動只能在本塊內或移動到下一塊因此爲n 1/2,共n次,共n 3/2,而r的移動每一個塊內 l 對應的r逐漸增加,最多從1到n,因此總的移動爲n 3/2。
所以總的時間複雜度爲O(n 3/2).
——————————————————————————————————————————————————————————
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=5e4+7;
const int maxm=5e4+7;
int n,m;
ll sz[maxn],ans,da[maxm],daa[maxm];
int l,r,qrtn;
int cs[maxn]={0};
struct que
{
    int l,r,id;
}q[maxm];
bool mycmp(que a,que b)
{
    if(a.l/qrtn<b.l/qrtn || (a.l/qrtn==b.l/qrtn && a.r<b.r))return 1;
    return 0;
}
ll c(ll x)
{
    if(x<2)return 0;
    return x*(x-1)/2;
}
void del(int pos)
{
    int tp=--cs[sz[pos]];
    ans-=c(tp+1);
    ans+=c(tp);
}
void add(int pos)
{
    int tp=++cs[sz[pos]];
    ans-=c(tp-1);
    ans+=c(tp);
}
ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%lld",sz+i);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    qrtn=sqrt(0.5+n);
    sort(q,q+m,mycmp);
    l=0;r=-1;ans=0;
    for(int i=0;i<m;i++)
    {
        while(l<q[i].l)
        {
            del(l);l++;
        }
        while(l>q[i].l)
        {
            l--;add(l);
        }
        while(r<q[i].r)
        {
            r++;add(r);
        }
        while(r>q[i].r)
        {
            del(r);r--;
        }
        da[q[i].id]=ans;
        daa[q[i].id]=c(r-l+1);
    }
    for(int i=0;i<m;i++)
    {
        if(da[i]==0)
        {
            printf("0/1\n");
            continue;
        }
        ll tp=gcd(daa[i],da[i]);
        printf("%lld/%lld\n",da[i]/tp,daa[i]/tp);
    }
    return 0;
}
相關文章
相關標籤/搜索