ARC-114 C- Sequence Scores 计数
题意
给定长度为(n)的可重数列(A),(A)中元素的范围是(1-M)。
给定长度为(n)的可重数列(X),初始时刻元素全部为(0)
每次可以进行操作([l,r,x])对数列(X)的([l,r])区间([l,r])取操作(max(v,x[i]))
记某可重数列(A(w))最少经过(c)次操作可以得到(X)
问(sum c_{A_(i)}) ,(A(i))为所有可能的可重数列
[1leq n,mleq5000
]
分析
动态地认为每次向已经存在的(A)中加入一个数来记录答案。
假设当前加入的数为(x)
- 若(x)不在(A)中出现,(c++),因为必须花费一次来得到这个数
- 若(x)在(A)中出现过,(x_{pre})到(x)之间的所有数都大于(x),这样不需要额外花费1,因为可以一次性得到这些(x)
- 若(x)在(A)中出现过,(x_{pre})到(x)之间不是所有数都大于(x),那么必然要额外花费1
因此新加入的数带来的贡献全部由1和3组成,记贡献为(S_{i,x})
[S_{i,x} = m^{i - 1} - sum_{k = 1}^{i - 1}(m - x)^{i - 1 - k} imes m^{k - 1}
]
其中(k)表示出现(x)的位置,那么([k,i - 1])之间的所有数取值范围是([x + 1,m])。其他地方数任意取即可。
[ans_i = sum_{x = 1}^{m} ans_{i - 1} + S_{i,x}
]
复杂度(O(nm))
其中(sum_{k})的和式可以用前缀和递推
代码
int main(){
int n = rd();
int m = rd();
vector<ll> sum(m + 1,1);
ll ans = m;
ll pre = 0;
vector<ll> pow(n + 1);
pow[0] = 1;
for(int i = 1;i <= n;i++)
pow[i] = (ll)pow[i - 1] * m,pow[i] %= MOD;
for(int i = 2;i <= n;i++){
pre = ans;
ans = 0;
if(i > 2) {
for(int j = 1;j <= m;j++)
sum[j] = (ll) sum[j] * (m - j) % MOD + pow[i - 2],sum[j] %= MOD;
}
for(int j = 1;j <= m;j++)
ans += ((ll)pre + pow[i - 1] - sum[j] + MOD) % MOD,ans %= MOD;
}
cout << ans;
}