Atcoder Regular Contest 128 F - Game against Robot(组合数学)
首先考虑对于固定的 \(p\) 怎么求答案。其实就是 UOJ 749 稳健型选手。考虑维护一个堆,按 \(p\) 从大到小的顺序,每次加入两个数后 pop 出最大的元素累加入答案,最后的总和就是 snuke 最大得分。
接下来考虑计数。首先将 \(a\) 从大到小排序,由于贡献独立,所以我们可以统计出 \(a_k\) 被加入答案的次数 \(c_k\),答案就是 \(\sum a_kc_k\),直接求 \(c_k\) 貌似有点困难,因为我们很难限制每次取出的最大值恰好是 \(a_k\)。因此我们考虑求另一个东西:\(\le a_k\) 的数被累加入答案的总次数 \(d_k\),这样求出 \(d\) 后差分回 \(c\) 后也可求出答案。
考虑怎样求 \(d_k\),考虑对于一个排列构造一个 01 串——如果 \(p\) 值第 \(i\) 大的数 \(<a_k\) 那么 01 串第 \(i\) 位为 \(0\),反之第 \(i\) 位为 \(1\),这样相当于我们要对所以 \(n-k\) 个 \(0\) 和 \(k\) 个 \(1\) 的 01 串统计求和后乘以 \(k!(n-k)!\)。考虑一个 01 串的答案是什么,显然对于所有偶数 \(i\),在前 \(i\) 位中至多只有 \(\dfrac{i}{2}\) 个 \(1\) 能活下来,因此不可避免地要寄了的 \(1\) 的个数是 \(k-\max\limits_{i\text{ is even}}(pre_i-\dfrac{i}{2})\),其中 \(pre_i\) 表示前 \(i\) 位中 \(1\) 的个数。
前面这个 \(+k\) 的贡献显然容易处理。考虑后面这东西怎么求。\(i\) 是偶数的条件够呛,考虑将 \(i\) 的定义域改到 \([0,n]\) 中。怎么办呢?将序列中的 \(0\) 变成 \(-1\) 做一遍前缀和得到 \(pre'\),这样这东西又等价于 \(\max\lfloor\dfrac{pre'_i}{2}\rfloor\),之所以包含了奇数的情况,因为显然有奇数 \(i\),\(pre'_i\) 也是奇数并且 \(pre'_i\le pre'_{i-1}+1\),因此 \(\lfloor\dfrac{pre'_i}{2}\rfloor\le\lfloor\dfrac{pre'_{i-1}}{2}\rfloor\)。这样看起来就舒服多了。
最后再考虑如何计算这个 \(\max\) 之和,先考虑一个暴力做法大概就是枚举一个 \(v\) 然后计算这个 \(\max\ge v\) 的方案数,容斥一下变成 \(<v\) 之后发现是一个类似于卡特兰数的东西,这样对单个 \(v\) 可以 \(O(1)\),而我们还可以发现这些组合数的上指标都是 \(n\),下指标是公差 \(2\) 的等差数列,对 \(i\) 是奇数和偶数分别处理 \(\dbinom{n}{i}\) 的前缀和后即可对所有 \(v\) 求和。
时间复杂度线性。
const int MAXN=1e7;
const int MOD=998244353;
int n,a[MAXN+5],fac[MAXN+5],ifac[MAXN+5],f[MAXN+5];
void init_fac(int n){
for(int i=(fac[0]=ifac[0]=ifac[1]=1)+1;i<=n;i++)ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
}
int binom(int n,int k){if(n<0||k<0||n<0)return 0;return 1ll*fac[n]*ifac[k]%MOD*ifac[n-k]%MOD;}
int pre[MAXN+5];
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&n);init_fac(MAXN);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+n+1,greater<int>());
pre[0]=1;pre[1]=n;
for(int i=2;i<=n;i++)pre[i]=(pre[i-2]+binom(n,i))%MOD;
for(int k=1;k<=n;k++){
int sum=0,tot=k-(n-k),L=max(tot/2,0),R=k-1;
// for(int t=max(tot/2,0);t<k;t++)sum=(0ll+sum+binom(n,k)-binom(n,n/2+(4*t+4-tot)/2)+MOD)%MOD;
sum=(sum+1ll*(R-L+1)*binom(n,k)%MOD);
int LL=n/2+2*L+2-tot/2,RR=n/2+2*R+2-tot/2;
chkmin(RR,n-(RR%2+2)%2);chkmax(LL,(LL%2+2)%2);
sum=(sum-pre[RR]+MOD)%MOD;if(LL-2>=0)sum=(sum+pre[LL-2])%MOD;
f[k]=1ll*sum*fac[k]%MOD*fac[n-k]%MOD;
// printf("%d %d\n",k,f[k]);
}int ans=0;
for(int i=1;i<=n;i++)ans=(ans+1ll*(f[i]-f[i-1]+MOD)*a[i])%MOD;
printf("%d\n",ans);
return 0;
}