传出球的最小值不为 \(0\) 时,可以将所有人传出球的数量同时减一,得到的序列不变。
所以得出结论,所有人传出的球的数量的最小值必定为 \(0\)。
观察答案的实际含义:最终序列中从每个人手上选出一个球的方案数。
每个人的球只有两种来源:自己原来没有传出去的球/上一个人传过来的球。启示我们记录球的来源以 DP。
状态的定义有点奇怪:\(f_{i,0}\) 表示第 \(i\) 个人从自己处选择球且不计第 \(i\) 个人选择方案时前 \(i-1\) 个人的选球方案数,\(f_{i,1}\) 表示第 \(i\) 个人从前面人选择球且计入第 \(i\) 个人的选球方案时的方案数。
转移分为四种情况:
\((i,0)\to (i+1,0)\),此时要计算第 \(i\) 个人的选择方案,剩下几个球就有几种转移方案,转移系数为 \(\sum_{k=1}^{a_i}k\)。
\((i,0)\to(i+1,1)\),此时要计算第 \(i\) 个人和第 \(i+1\) 个人的选择方案,转移系数为 \(\sum_{k=1}^{a_i-1}k\times (a_i-k)\)。
\((i,1)\to (i+1,0)\),此时要计算第 \(i\) 个人的选择方案,转移系数为送出几个球,即 \(\sum_{k=0}^{a_i}1=a_i+1\)。
\((i,1)\to (i+1,1)\),此时要计算第 \(i+1\) 个人的选择方案,转移系数为 \(\sum_{k=1}^{a_i}k\)。
因为是一个环,那么可以枚举 \(1\) 的决策然后开始 DP。
注意这样子统计出来的并不要求传球数最小为 \(0\) 那么可以再编一个传球数最小为 \(1\) 的式子写出来即可。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005, mod = 998244353, inv2 = (mod + 1) / 2, inv6 = (mod + 1) / 6;
int n;
int a[N], f[N][2];
void add(int &a, int b) {
a += b;
if (a >= mod) a -= mod;
if (a < 0) a += mod;
}
int calc1(int x) { return 1ll * x * (x + 1) % mod * inv2 % mod; }
int calc2(int x) { return 1ll * x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod; }
int solve(int x, int y) {
memset(f, 0, sizeof f);
f[1][x] = 1;
for (int i = 1; i <= n; ++i) {
add(f[i + 1][0], 1ll * f[i][0] * calc1(a[i] - y) % mod), add(f[i + 1][0], 1ll * f[i][1] * (a[i] - y + 1) % mod);
add(f[i + 1][1], 1ll * f[i][0] * ((1ll * a[i] * calc1(a[i]) % mod - calc2(a[i]) + mod) % mod) % mod), add(f[i + 1][1], 1ll * f[i][1] * calc1(a[i]) % mod);
}
return f[n + 1][x];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
int ans = 0; add(ans, solve(1, 0)), add(ans, solve(0, 0));
add(ans, -solve(1, 1)), add(ans, -solve(0, 1));
printf("%d", ans);
return 0;
}
标签:方案,个人,int,sum,ARC124E,转移,mod
From: https://www.cnblogs.com/Kobe303/p/16735562.html