【bzoj2038】[2009國家集訓隊]小Z的襪子(hose) (莫隊算法)

題目描述

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

輸入輸出格式

輸入文件第一行包含兩個正整數N和M。N爲襪子的數量,M爲小Z所提的詢問的數量。接下來一行包含N個正整數Ci,其中Ci表示第i只襪子的顏色,相同的顏色用相同的數字表示。再接下來M行,每行兩個正整數L,R表示一個詢問。
包含M行,對於每個詢問在一行中輸出分數A/B表示從該詢問的區間[L,R]中隨機抽出兩隻襪子顏色相同的概率。若該概率爲0則輸出0/1,否則輸出的A/B必須爲最簡分數。(詳見樣例)

考慮區間[L,R]所求概率。
總可能數爲(RL)(RL1) (第一次取R-L種,第二次R-L-1種)
對於取出兩雙的可能性,假設L,R中襪子的顏色分別爲a,b,c,d……,而出現的個數爲cnt[a],cnt[b],cnt[c],cnt[d]……
那麼顯然,ans=(cnt[a](cnt[a]1))+(cnt[b](cnt[b]1))++(cnt[](cnt[]1))(RL)(RL1)
那麼化簡後得
ans=cnt[a]2+cnt[b]2++cnt[]2(cnt[a]+cnt[b]++cnt[])(RL)(RL1)
=cnt[a]2+cnt[b]2++cnt[]2(RL+1)(RL)(RL1)

那麼需要做的就是維護區間L,R中所有cnt[]^2,線段樹感覺不可做。

那麼此時就需要一個神奇的算法,莫隊算法。
這個算法是由國家隊的大佬發明的(%%%),可以處理一些離線而且可以由[L,R]O(1)得到[L+-1,R+-1]的詢問。
首先我們考慮一下對於詢問[L1,R1],[L2,R2],假如滿足如上O(1)時間的限制,那麼我們從[L1,R1]到[L2,R2]一共需要|L1-L2|+|R1-R2|次的操作。
即相當於兩個點的曼哈頓距離。
對於三個詢問
1,3
3,5
2,4
顯然我們先處理第一個詢問,再處理第三個,最後處理第二個的話總的曼哈頓距離是最小的。
莫隊算法就是一種將詢問離線並進行排序後再暴力的算法,時間複雜度卻可以得到顯著的提升。
這個排序可以怎麼做呢,顯然假如把所有詢問看成點那麼就是求二維平面最小曼哈頓距離生成樹,但是代碼的複雜度較高。
感興趣可以看這個大佬的
而另一種簡便的方式便是分塊。
按照每個詢問左端點與右端點所在的塊進行排序,左端點爲第一關鍵字,右端點爲第二關鍵字。
至於複雜度分析,我就貼一下黃學長寫的

1、i與i+1在同一塊內,r單調遞增,所以r是O(n)的。由於有n^0.5塊,所以這一部分時間複雜度是n^1.5。
2、i與i+1跨越一塊,r最多變化n,由於有n^0.5塊,所以這一部分時間複雜度是n^1.5
3、i與i+1在同一塊內時l變化不超過n^0.5,跨越一塊也不會超過n^0.5,忽略*2。由於有m次詢問(和n同級),所以時間複雜度是n^1.5
於是就是O(n^1.5)了

#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define pa pair<int,int>
const int maxn=50010;
const int INF=1e9+7;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
    char ls;ll x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------------------------------*/
ll n,m,now;
ll a[maxn],belong[maxn],cnt[maxn],ans[maxn],tot[maxn];
struct kaga
{
    int l,r;
    int id;
    bool friend operator <(kaga a,kaga b)
    {
        if(belong[a.l]==belong[b.l])return a.r<=b.r;
        else return a.l<b.l; 
    }
}q[maxn];
void update(int x,int d)
{
    now-=cnt[x]*cnt[x];
    cnt[x]+=d;
    now+=cnt[x]*cnt[x];
}
int main()
{
    n=read();m=read();
    memset(cnt,0,sizeof(cnt));
    int block=sqrt(n);
    fer(i,1,n)
    belong[i]=(i-1)/block+1;
    fer(i,1,n)a[i]=read();
    fer(i,1,m)
    {
        q[i].l=read(),q[i].r=read();
        q[i].id=i;
    }
    sort(q+1,q+m+1);
    //fer(i,1,m)cout<<q[i].l<<" "<<q[i].r<<endl;
    int nowl=1,nowr=0;
    fer(i,1,m)
    {
        ll l=q[i].l,r=q[i].r;
        ll id=q[i].id;
        if(l==r)
        {
            ans[id]=0;
            tot[id]=1;  
        }
        fer(j,nowr+1,r)update(a[j],1);

        far(j,nowr,r+1)update(a[j],-1);

        fer(j,nowl,l-1)update(a[j],-1);

        far(j,nowl-1,l)update(a[j],1);

        nowl=l;nowr=r;

        ans[id]=now-(r-l+1);
        tot[id]=(r-l+1)*(r-l);
        ll gcd=__gcd(ans[id],tot[id]);
        ans[id]/=gcd;
        tot[id]/=gcd;
    }
    fer(i,1,m)
    printf("%lld/%lld\n",ans[i],tot[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章