1. 程式人生 > >Luogu 1494 - 小Z的襪子 - [莫隊算法模板題]

Luogu 1494 - 小Z的襪子 - [莫隊算法模板題]

query 每天 urn dot 相同 假設 == -o con

題目鏈接:https://www.luogu.org/problemnew/show/P1494

題目描述

作為一個生活散漫的人,小Z每天早上都要耗費很久從一堆五顏六色的襪子中找出一雙來穿。終於有一天,小Z再也無法忍受這惱人的找襪子過程,於是他決定聽天由命……

具體來說,小Z把這N只襪子從1到N編號,然後從編號L到R(L 盡管小Z並不在意兩只襪子是不是完整的一雙,甚至不在意兩只襪子是否一左一右,他卻很在意襪子的顏色,畢竟穿兩只不同色的襪子會很尷尬。

你的任務便是告訴小Z,他有多大的概率抽到兩只顏色相同的襪子。當然,小Z希望這個概率盡量高,所以他可能會詢問多個(L,R)以方便自己選擇。

然而數據中有L=R的情況,請特判這種情況,輸出0/1。

輸入輸出格式

輸入格式:

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

輸出格式:

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

輸入輸出樣例

輸入樣例#1:
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
輸出樣例#1:
2/5
0/1
1/1
4/15

說明

30%的數據中 N,M ≤ 5000;

60%的數據中 N,M ≤ 25000;

100%的數據中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

題解:

直接上線段樹的話,由於不滿足加和性質

假設現在要計算區間 [ l , r ] 裏每一種顏色的襪子的數量,此時我們已經分別維護好了左右兩邊區間 [ l , mid ] 和 [ mid+1 , r ] 中每一種顏色的襪子的數量,然後開始向上維護:

就會發現,依然要把每種顏色的襪子在左右區間的數量加起來才能完成對一個父節點的維護,我們並不能在O(1)的時間內完成對線段樹單一節點的維護。

但此時,若已知一個區間 [ l , r ] 的情況,我們可以在O(1)的時間內確定的有 [ l , r+1 ] 和 [ l , r-1 ] 和 [ l-1 , r ] 和 [ l+1 , r ] 的情況,

假設我們現在已知 [ l , r ] 的情況,需要計算的是 [ l‘ , r‘ ] 的情況,易得所需時間復雜度為O( | l - l‘ | + | r - r‘ | ),

那麽怎麽樣能夠在較短的時間內以較少的代碼量回答Query呢?

先對序列sqrt(N)分塊,然後以詢問的區間左端點所在的分塊的序號為第一優先級,區間右端點的大小為第二優先級進行排序,然後按順序計算,復雜度就會大大降低。

按上述順序,從第i個區間到第i+1個區間有如下情況:

那麽假設某個區間有X(X≥2)只不同襪子,那麽任取兩只襪子的可能數是 $C_X^2$ ,

假設區間內的襪子有1~K共K種顏色,假設第k個顏色的襪子數為num[k],那麽取到同種顏色的兩只襪子的可能數是 $C_{num\left[ 1 \right]}^2 + C_{num\left[ 2 \right]}^2 \cdots C_{num\left[ K \right]}^2$ ,

根據組合數公式可知 $C_n^2 = \frac{{n\left( {n - 1} \right)}}{2}$ ,

所以取到兩只同色襪子的概率為 $\frac{{num\left[ 1 \right]\left( {num\left[ 1 \right] - 1} \right) + \cdots + num\left[ K \right]\left( {num\left[ K \right] - 1} \right)}}{{X\left( {X - 1} \right)}}$ ,

即 $\frac{{num\left[ 1 \right]{}^2 + \cdots + num\left[ K \right]^2 - X}}{{X\left( {X - 1} \right)}}$ 。

由於 $X = R - L + 1$ ,我們實際要查詢的就是 $Q\left( {L,R} \right) = \sum\limits_{k = 1}^K {num\left[ k \right]^2 }$ ,

那麽,

若區間轉移時增加一只顏色為 $c$ 的襪子:

$Q\left( {L,R + 1} \right) = Q\left( {L - 1,R} \right) = Q\left( {L,R} \right) - num\left[ c \right]^2 + \left( {num\left[ c \right] + 1} \right)^2 = Q\left( {L,R} \right) + 2 \times num\left[ c \right] + 1$

若區間轉移時減少一只顏色為 $c$ 的襪子:

$Q\left( {L,R - 1} \right) = Q\left( {L{\rm{ + }}1,R} \right) = Q\left( {L,R} \right) - num\left[ c \right]^2 + \left( {num\left[ c \right] - 1} \right)^2 = Q\left( {L,R} \right) - 2 \times num\left[ c \right] + 1$

AC代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=50000+10;
const int MAXM=50000+10;

class Interval
{
public:
    int block; //表示左端點所在塊
    int id; //表示詢問的原始順序
    int l,r;
    bool operator <(const Interval &oth) const
    {
        return (this->block == oth.block) ? (this->r < oth.r) : (this->block < oth.block);
    }
}itv[MAXM];

int n,m;
int color[MAXN],num[MAXN];
LL up[MAXN],down[MAXN];

inline LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int len=sqrt(n);
        for(int i=1;i<=n;i++) scanf("%d",&color[i]);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&itv[i].l,&itv[i].r);
            itv[i].block=itv[i].l/len;
            itv[i].id=i;
            down[i]=(itv[i].r-itv[i].l)/2*(itv[i].r-itv[i].l+1);
        }
        sort(itv+1,itv+m+1);

        memset(num,0,sizeof(num));
        int pl=1,pr=0;
        LL Q=0;
        for(int i=1;i<=m;i++)
        {
            if(itv[i].l==itv[i].r)
            {
                down[itv[i].id]=1;
                up[itv[i].id]=0;
                continue;
            }

            down[itv[i].id]=(LL)(itv[i].r-itv[i].l+1)*(itv[i].r-itv[i].l);

            if(pr<itv[i].r)
            {
                for(int j=pr+1;j<=itv[i].r;j++)
                {
                    Q=Q+2*num[color[j]]+1;
                    num[color[j]]++;
                }
            }
            if(pr>itv[i].r)
            {
                for(int j=pr;j>itv[i].r;j--)
                {
                    Q=Q-2*num[color[j]]+1;
                    num[color[j]]--;
                }
            }
            if(pl>itv[i].l)
            {
                for(int j=pl-1;j>=itv[i].l;j--)
                {
                    Q=Q+2*num[color[j]]+1;
                    num[color[j]]++;
                }
            }
            if(pl<itv[i].l)
            {
                for(int j=pl;j<itv[i].l;j++)
                {
                    Q=Q-2*num[color[j]]+1;
                    num[color[j]]--;
                }
            }

            up[itv[i].id]=Q-(itv[i].r-itv[i].l+1);

            pl=itv[i].l;
            pr=itv[i].r;
        }

        for(int i=1;i<=m;i++)
        {
            LL g=gcd(up[i],down[i]);
            printf("%lld/%lld\n",up[i]/g,down[i]/g);
        }
    }
}

Luogu 1494 - 小Z的襪子 - [莫隊算法模板題]