題解-CSA Beta Round#1 Number Elimination

Problem

CSA-Beta Round#3c++

題意概要:給定 \(n\) 個數組成的序列,定義一次操做:git

  • 在當前序列中選擇兩個數,將其中較小的數從序列中刪除(若兩個數相同,則刪除在序列中更靠前的)
  • 較大的數爲這次操做的花費

進行 \(n-1\) 次操做後只剩一個數,求有多少種不一樣的執行方案,知足其花費爲全部方案中花費最少的數組

Solution

發現網上沒有什麼CSA方面的資料?spa

首先有一個貪心,就是將數字排序離散後壓縮一下,若數字 \(i\)\(s_i\) 個,則必定先來 \(s_i-1\) 次花費爲零的操做(每次選擇兩個 \(i\)),剩下的一個 \(i\) 必定是被一個 \(i+1\) 刪除code

而後考慮用操做的拓撲圖來統計……沒轍,正解是序列 \(dp\)排序

先設 \(dp[i]\) 表示 \(i\) 個相同的數字互相消除直至只剩一個數字的方案數,易得 \(dp[n] = \prod_{i=2}^n\frac {i(i-1)}2\)get

再考慮設 \(f[i]\) 爲前 \(i\) 堆數字已經消成只剩一個的方案數it

考慮在處理完前 \(i-1\) 堆的基礎上加入第 \(i\) 堆,不妨枚舉在第 \(i-1\) 堆的最後一個元素被消除以前有多少個 \(i\) 被消除了,易得:io

\[f[i]=\sum_{x=0}^{s_i-1}f[i-1]\cdot dp[s_i](s_i-x)\binom {\sum_{j=1}^{i-1}s_i-1+x}x\]class

一個個解釋:

  • \(f[i-1]\):前 \(i-1\) 堆消成一個 \(i-1\) 的方案數
  • \(dp[s_i]\):這 \(s_i\)\(i\) 消成一個的方案數
  • \(s_i-x\):前 \(i-1\) 堆消剩下的那一個 \(i-1\) 須要從剩下的 \(s_i-x\)\(i\) 中挑一個消它
  • \(\binom {\sum_{j=1}^{i-1}s_i-1+x}x\):考慮將這 \(x\) 個提早消的融入前面的消除序列(這裏不使用排列是由於這 \(x\)\(i\) 內部之間的先後關係已經在 \(dp[s_i]\) 中考慮過了)

總的來講,就是將 \(s_i\) 分爲兩段,一份放入前面一塊兒大排隊,另外一份用來消剩下的那個 \(i-1\)

複雜度 \(O(\sum s_i)=O(n)\)

Code

#include <bits/stdc++.h>
typedef long long ll;

inline void read(int&x){
    char ch=getchar();x=0;while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
}

const int N = 101000, p = 1e9+7;
int a[N], sum[N], f[N], dp[N], n;

inline int qpow(int A, int B) {
    int res = 1; while(B) {
        if(B&1) res = (ll)res * A%p;
        A = (ll)A * A%p, B >>= 1;
    } return res;
}

int fac[N], inv[N];
inline int C(const int nn, const int mm) {return (ll)fac[nn] * inv[mm]%p * inv[nn-mm]%p;}

int main() {
    read(n);
    for(int i=1;i<=n;++i) read(a[i]);
    std::sort(a+1,a+n+1);
    
    dp[0] = dp[1] = fac[0] = fac[1] = 1;
    for(int i=2;i<=n;++i) {
        fac[i] = (ll)fac[i-1] * i%p;
        dp[i] = ((ll)i * (i-1) >> 1) * dp[i-1]%p;
    }
    inv[n] = qpow(fac[n], p-2);
    for(int i=n;i;--i) inv[i-1] = (ll)inv[i] * i%p;
    
    int t = 0;
    for(int i=1,j=1;i<=n;i=j) {
        while(a[i] == a[j] and j <= n) ++j;
        a[++t] = j - i, sum[t] = (a[t] + sum[t-1])%p;
    }
    n = t;
    
    f[1] = dp[a[1]];
    for(int i=2;i<=n;++i) {
        for(int x = 0; x < a[i]; ++ x)
            f[i] = (f[i] + (ll)C(sum[i-1]-1+x,x) * (a[i]-x))%p;
        f[i] = (ll)f[i] * f[i-1]%p * dp[a[i]]%p;
    }
    printf("%d\n", f[n]);
    return 0;
}
相關文章
相關標籤/搜索