:::info[Description]
Link.
:::
:::infoSolution
记 Ci 表示当前第 i 个计数器的数值。考虑用 K=imax(Ai−Ci) 来记录一组计数器的状态。当且仅当 K≤0 时一组计数器才是满足题目要求的。考虑对于一个状态 K,若选择了第 j 个计数其,则 K 状态就会变成 min(K−1,Aj)。
设 fi 表示当前的状态 K=i 时,期望还需要操作多少次。记 Si 表示满足 Aj<i 的 j 的数量,则选中这 Si 个位置时新的状态是 K−1,否则新的状态是对应的 Aj 值。因此转移方程可以被写作:fi=1+nSifi−1+n1Aj≥i∑fAj,可以做到 O(V) 时间复杂度解决问题。
考虑进一步优化。套路的对相邻的两个 fi 做差:fi+1 的值可以被表示为 fi+1=1+nSifi+n1Aj≥i∑fAj 的形式,两式做差可得 gi=fi−fi−1 的递推式是 gi+1=nSigi。再人为的定义 fAn+1=fAn+1,gAn+1=1,则答案可以被表示为 fAn−f0=j=1∑Angj 的形式。
现在考虑一段 k∈(Ai−1,Ai],若满足 Ai>Ai−1,则这一段中恰好有前 i−1 个 Aj 满足 Aj<k,因此 Sk=i−1。设 p=i−1n,d=Ai−Ai−1,则若当前已经知道 gAi+1=G,则必有 gAi=Gp,gAi−1=Gp2,…,gAi−1+1=Gpd。因此这一整段对答案的贡献就可以被表示为等比数列求和的形式即 G(p+p2+…+pd)=Gp−1p(pd−1) 的形式,然后更新 G←Gpd 即可。
于是问题的时间复杂度被优化到 O(nlogV) 的级别,可以通过该题。
:::
:::success[Code]
namespace lowspeed_song {
int a[N], f[N], inv[N];
inline void init() {
inv[0] = inv[1] = 1;
for (int i = 2; i < N; ++i) inv[i] = mod - inv[mod % i] * (mod / i) % mod;
}
inline void sol() {
int n; cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
int all = 0;
for (int i = n - 1; i; --i) {
f[i] = (power(n * inv[i] % mod, a[i + 1] - a[i], mod) * f[i + 1] % mod + (power(n * inv[i] % mod, a[i + 1] - a[i], mod) - 1) * inversion(n * inv[i] % mod - 1) % mod * (n - all + mod) % mod * inv[i]) % mod;
all = (all + f[i]) % mod;
} cout << f[1] << '\n';
}
} // namespace lowspeed_song
:::
难度:黑-
鲜活指数:8
喜欢的话,留下你的评论吧~