1. 程式人生 > 其它 >The 2020 ICPC Asia Macau Regional Contest A - Accelerator

The 2020 ICPC Asia Macau Regional Contest A - Accelerator

題目連結:https://codeforces.com/gym/103119/problem/A

推薦部落格:https://fanfansann.blog.csdn.net/article/details/118528285

題意:(複製上面推薦部落格的)

思路:

  上述式子化簡,可以得到a1*a2*...*an + a2*a3*...*an + ... + an

  求該式子所有排列的期望,就相當於:所有排列的貢獻之和 / 排列數

  全排列個數顯然是 n!

  關鍵在於怎麼計算所有排列之和,先畫畫圖,假設陣列是【1,2,3】

 

  那麼答案就是(15+14+12+10+10+9) / 6 = 70 / 6

  按列來考慮,假設 f(x)是 “ 長度為x的所有排列的貢獻和 ”

,則f(3) = 6*6 = 36,f(2) = (6+3+2) * 2 = 22, f(1) = (3+2+1)*2 = 12, 總和就是f(1)+f(2)+f(3) = 70

  問題就轉化成如何快速求 f(x)

  假設求f(2),那麼所有排列就是{【1,2】,【1,3】,【2,3】,【2,1】,【3,1】,【3,2】}

  可見,就是在3個數中任選2個出來,就可以組成一個序列,而且每個序列的數字可以交換位置。

  這像什麼呢?在一堆數中隨意挑幾個數出來合在一起求貢獻,應該要想到多項式相乘

  對於單個ai,我們先構造出他的多項式,

  關於冪次,ai只有取和不取,所以冪次設成0和1,冪次為0代表不取這個數,冪次為1代表取這個數,

  關於係數,當不取ai的時候,無貢獻,相當於乘1,當取ai的時候,貢獻為ai,所以冪次為0的係數為1,冪次為1的係數為ai

  單個ai的多項式如圖

  

  我們把所有ai的多項式乘起來,那麼結果多項式中,冪次為x的係數就是長度為x的排列的貢獻和(注意:這裡的【1,2】和【2,1】是一種排列)

  舉個例子,假設只有a1和a2

  多項式相乘:(1 * X0 + a1 * x1) * (1 * X0 + a2 * x1) == 1 * X0 + (a1+ a2)* x1 + a1 * a2 * X2

  冪次為2的係數是a1* a2, 並沒有出現a2* a1

  這和我們的f(x)還有點差距,少了一些排列的貢獻和,但是注意到沒有算進去的排列,都可以通過改變順序轉換成某個算出來的排列,比如a2

* a1可以改變順序變成 a1* a2

  可以通過算期望來計算出f(x),假設g(x)為冪次為x的係數,期望 = 權重* 概率 ,權重為g(x),概率為 n! / C(n,x) ,化簡一下概率就是 x! * (n-x)!

  說明一下這個概率是怎麼算的,n!表示一共會出現的排列數(可以看上面的表格,不管x是多少,排列數都是一樣的),C(n,x) 表示n個數裡面選x個組成的排列數,即g(x)是由多少項相加得到

  g(x)乘這個概率就相當於把沒算的部分加上去,得到f(x)

  

  至此,我們可以算出所有的f(x),那麼答案就是(f(1) + f(2) + ... + f(n)) / n!

  算g(x)可以用NTT,n個多項式相乘,可以看成線段樹的操作,每個節點看成一個多項式,線段樹每個最底層的節點相當於一個多項式,往上合併,兩個節點合併的時候使用NTT合併,一直合到根節點就可以算出n個多項式相乘的結果

  看了很多部落格,這種操作應該叫分治NTT,不過我喜歡看成線段樹來理解。總的時間複雜度是O(n * logn * logn

  注意:編譯器選這個有優化的,不然大概率TLE

  

程式碼:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll n;
const ll p=998244353,G=3,Gi=332748118,mod = 998244353;
ll limit=1,L=0,r[maxn];
ll a[maxn],jie[maxn],q[maxn],f1[maxn],f2[maxn];
ll qpow(ll a,ll n) {
    ll c=1;
    while(n) {
        if(n&1) c = c * a % mod;
        n >>= 1;
        a = a * a % mod;
    }
    return c;
}
ll C(ll n,ll m) {
    return jie[n] * q[m] % p * q[n-m] % p;
}
void init(ll len) {
    L = 0;
    limit = 1;
    while(limit<=len) limit<<=1,L++;
    for (int i=0; i<limit; ++i) 
        r[i] = (r[i>>1]>>1) | ((i&1)<<(L-1));
}
void NTT(ll *A,int type) {
    for (int i=0; i<limit; ++i)
        if(i<r[i]) swap(A[i],A[r[i]]);
    for (int mid=1; mid<limit; mid<<=1) {
        ll Wn = qpow(type==1?G:Gi,(p-1)/(mid<<1));
        for (int j=0; j<limit; j+=(mid<<1)) {
            ll w=1;
            for (int k=0; k<mid; k++,w=(w*Wn)%p) {
                ll x=A[j+k],y=w*A[j+k+mid]%p;
                A[j+k] = (x+y)%p;
                A[j+k+mid]=(x-y+p)%p;
            }
        }
    }
}
void gao(int l,int r,vector<ll> &f) {
    if(l == r) {
        f[0] = 1;
        f[1] = a[l];
        return;
    }
    int mid = l + r >> 1;
    int len1 = mid - l + 1, len2 = r - mid;
    vector<ll>w1(len1+7,0),w2(len2+7,0);
    gao(l,mid,w1);
    gao(mid+1,r,w2);
    for (int i=0; i<=len1; ++i) f1[i] = w1[i];
    for (int i=0; i<=len2; ++i) f2[i] = w2[i];
    int len = len1 + len2;
    init(len);
    for (int i=len1+1; i<limit; ++i) f1[i] = 0;
    for (int i=len2+1; i<limit; ++i) f2[i] = 0;
    NTT(f1,1);
    NTT(f2,1);
    for (int i=0; i<=limit; ++i) f1[i] = (f1[i] * f2[i]) % p;
    NTT(f1,-1);
    ll inv = qpow(limit,p-2);
    for (int i=0; i<=len; ++i) f[i] = f1[i] * inv % p;
}
void solve() {
    scanf("%lld",&n);
    for (int i=1; i<=n; ++i) scanf("%lld",&a[i]);
    vector<ll>b(n+7,0);
    gao(1,n,b);
    ll ans = 0;
    for (int i=1; i<=n; ++i) {
        ans = (ans + b[i] * jie[i] % p * jie[n-i] % p + p) % p;
//        printf("%d %lld %lld\n",i,jie[n] % p * qpow(C(n,i),p-2) % p,b[i]);
    }
//    printf("%lld\n",ans);
    printf("%lld\n",ans * q[n] % p);
}
int main() {
    jie[0] = 1;
    for (int i=1; i<=100000; ++i) jie[i] = jie[i-1] * i % p; 
    q[100000] = qpow(jie[100000],p-2);
    for (int i=100000-1; i>=0; --i) q[i] = q[i+1] * (i+1) % p; 
    int t;
    scanf("%d",&t);
    while(t--) {
        solve();
    }
    return 0;
}