PKUSC2018 最大前缀和

最大前缀和

小 C 是一个算法竞赛爱好者,有一天小 C 遇到了一个非常难的问题:求一个序列的最大子段和。

但是小 C 并不会做这个题,于是小 C 决定把序列随机打乱,然后取序列的最大前缀和作为答案。

小 C 是一个非常有自知之明的人,他知道自己的算法完全不对,所以并不关心正确率,他只关心求出的解的期望值,现在请你帮他解决这个问题,由于答案可能非常复杂,所以你只需要输出答案乘上 \(n!\) 后对 \(998244353\) 取模的值,显然这是个整数。

注:最大前缀和的定义:\(\forall i \in [1,n]\)\(\sum_{j=1}^{i}a_j\)的最大值。

对于\(100\%\)的数据,满足\(1\leq n\leq 20\)\(\sum_{i=1}^{n}|a[i]|\leq 10^9\)

题解

https://www.cnblogs.com/zjp-shadow/p/9141971.html

不难发现,成为最大前缀和位置 \(p\) 后面的所有前缀都不能 \(>0\)。如果 \(>0\) 那么后面必存在一点可以替换当前的答案。

有了这个思路,那我们可以把每个序列拆成两段考虑,而分割点就是位置 \(p\)。记 \(sum_s\)\(s\) 这个状态所有点的代数和。

  1. \(f(s)\) 表示 \(s\) 集合的排列的最大前缀和等于 \(sum_s\) 且其他前缀和严格更小的方案数。这里规定其他前缀和更小是为了避免算重。

    那么若 \(sum_s>0\),我们便可以把它放到某个单点的后面。

    \[ f(\{u\})\times f(s) \rightarrow f(\{u\}\cup s) \]

  2. \(g(s)\) 表示 \(s\) 集合的排列的所有前缀和都 \(\leq 0\) 的方案数。

    \(sum_{s\cup \{u\}}\leq 0\),那么我们便可以把 \(s\) 放到 \(u\) 的前面。

    \[ g(s)\times g(\{u\}) \rightarrow g(s\cup \{u\}) \]

  3. 答案为

    \[ ans=\sum_s sum_s\times f(s)\times g([n]\setminus s) \]

时间复杂度 \(O(2^n n)\)

CO int N=131072;
int omg[2][N],rev[N];

void NTT(poly&a,int dir){
    int lim=a.size(),len=log2(lim);
    for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int i=1;i<lim;i<<=1)
        for(int j=0;j<lim;j+=i<<1)for(int k=0;k<i;++k){
            int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]);
            a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
        }
    if(dir==1){
        int ilim=fpow(lim,mod-2);
        for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    }
}
poly operator*(poly a,poly b){
    int n=a.size()-1,m=b.size()-1;
    int lim=1<<(int)ceil(log2(n+m+1));
    a.resize(lim),NTT(a,0);
    b.resize(lim),NTT(b,0);
    for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
    NTT(a,1),a.resize(n+m+1);
    return a;
}

vector<int> a[2*N];
int tot,h[2*N],top;

IN bool cmp(int i,int j){
    return a[i].size()>a[j].size();
}
int main(){
    omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
    for(int i=2;i<N;++i){
        omg[0][i]=mul(omg[0][i-1],omg[0][1]);
        omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    }
    int n=read<int>(),w=read<int>();
    for(int i=2;i<=n;++i){
        int w=read<int>();
        a[++tot].resize(w+1);
        a[tot][0]=1,a[tot][w]=mod-1;
        h[++top]=tot;
    }
    make_heap(h+1,h+top+1,cmp);
    while(top>=2){
        int x=h[1];pop_heap(h+1,h+top+1,cmp),--top;
        int y=h[1];pop_heap(h+1,h+top+1,cmp),--top;
        a[++tot]=a[x]*a[y];
        h[++top]=tot,push_heap(h+1,h+top+1,cmp); // edit 1: cmp
    }
    int x=h[1],ans=0;
    for(int i=0;i<(int)a[x].size();++i)
        ans=add(ans,mul(mul(w,fpow(i+w,mod-2)),a[x][i]));
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/12143069.html
今日推荐