【題解】列隊春遊

卡布叻_周深發表於2024-06-29

思路分析

首先對於每一個 \(i\),可以處理不小於其的個數與小於其的個數(不包括自己),為了方便,分別表示成 \(big_i\)\(small_i\)

對於每一個小朋友,列舉其所在位置 \(j\),其站在這裡的機率為 \(\dfrac{1}{n}\)

第三層迴圈列舉其視野,即 \(pre_i\),為了方便用 \(k\) 表示,當然 \(k\le j\) 那麼我們需要:

  1. 其前 \(k-1\) 個人都比他矮。
  2. \(k\) 個人不低於他。

那麼顯然滿足條件 \(1\) 的機率為 \(\dfrac{A_{small_i}^{k-1}}{A_{n-1}^{k-1}}\)

滿足第 \(2\) 個條件的機率為 \(\dfrac{big_i}{n-k}\),即去了其自身和他前面 \(k-1\) 個人,剩下 \(n-k\) 個人中站在其前第 \(k\) 個人身高不小於他的機率。

那麼綜上所述對於一般情況下,對於第 \(i\) 個小朋友,他站在第 \(j\) 個位置,\(pre_i=k\) 時,其對於期望的貢獻為:

\[\dfrac{1}{n}\times\dfrac{A_{small_i}^{k-1}}{A_{n-1}^{k-1}}\times\dfrac{big_i}{n-k}\times k \]

同時其還存在特殊情況,沒有人能擋住他,即 \(j=k\) 時,要單獨計算其貢獻,此時貢獻就是沒有人能擋住他的機率 \(\times\) 其站在這個位置上的機率 \(\times\) 此時視野,和之前的區別就是沒了其前第 \(k\) 個人不比他低的機率了,那麼就是:

\[\dfrac{1}{n}\times\dfrac{A_{small_i}^{j-1}}{A_{n-1}^{j-1}}\times j \]

複雜度最佳化

此時的複雜度約為 \(O(n^4)\),雖然跑不滿但是不允許,我們發現 \(\dfrac{A_{small_i}^{k-1}}{A_{n-1}^{k-1}}\) 在一次 \(i\) 的迴圈中除了 \(k\) 這個變數別的變數始終不變,故此可以遞推,從而使複雜度降為 \(O(n^3)\)

遞推式很簡單,每次 \(k+1\) 時使其 \(\times\dfrac{small_i-k+1}{n-k}\) 即可。

注意\(\dfrac{A_{small_i}^{k-1}}{A_{n-1}^{k-1}}\) 不可能為負,所以為負時另其為 \(0\) 或直接結束本輪迴圈即可。

程式碼如下

#include<bits/stdc++.h>
// #define int long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=310;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,h[N],d[N];
double ans;
double P(int d,int m,int n)
{
    int up=1,down=1;
    for(int i=d-m+1;i<=n-m;i++) up*=i;
    for(int i=d+1;i<=n;i++) down*=i;
    return (double)up/(double)down;
}
signed main()
{
    read(n);
    for(int i=1;i<=n;i++) read(h[i]);
    sort(h+1,h+1+n);
    for(int i=1;i<=n;i++)
    {
        int t=lower_bound(h+1,h+1+n,h[i])-h;
        d[i]=t-1;  
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            double p=1; int v=d[i];
            for(int k=1;k<=j-1;k++)
                ans+=(double)k*p*(double)(n-d[i]-1)/(double)(n-k)/(double)(n),
                p*=(double)(v)/(double)(n-k),
                v-=(v>0);
            ans+=(double)(j)*(double)(p)/(double)(n);
        }
    printf("%.2lf",ans);
}

相關文章