AtCoder Beginner Contest 270 G,Ex
y1s1,G和Ex在推等比数列式子上是相似的。
G
前置知识:BSGS(其实就是根号讨论)
首先我们展开这个递归式:
感觉第一项有些难搞,故我们设\(F_{i}\),为:
之后我们套用BSGS的方法,设答案为\(kx+b\)。
则:
提取公因式:
我们先进行预处理:枚举\(i=0\)到\(\kappa -1\),将 \(A^{i+1} S+\sum_{j=1}^{i} A^j B\) 模\(P\)的值存入map中,如果已经在map中有这个值了,那么我们取\(i\)最小的(因为我们要让答案最小)。
之后枚举\(x\),查询\(\frac{G-\sum_{j=0}^{\kappa x} A^j B}{A^{\kappa x}} \mod P\)是否在map中,如果在,更新答案\(ans=\min (ans,\kappa x+C)\),其中\(C\)是map中预处理得到的最小的值。
若我们设\(\kappa =70000\),时间复杂度最优,大概是\(O(\sqrt n\log n)\)。
Ex
一道好题。
首先\(n\)个计数器是不好记录状态的。
一、简化并列出状态
我们设状态\(k=\max(A_{i}-C_{i})\),其中\(C_{i}\)是第\(i\)个计数器的值。
可以发现,若\(k\leq 0\),则我们就满足了\(C_{i}\geq A_{i}\),并且,其实我们\(k\)在每次操作最多减去\(1\),故当\(k=0\)时,我们就终止操作。
这样有什么好处呢?
转移是更方便的:
我们设\(r\),满足\(A_{r}< k\leq A_{r+1}\),则:
-
如果我们将第\(i\)个计数器清零,且\(i\leq r\),那么\(k=k-1\)。(因为\(A_{i}\)<k,\(A_{i}-C_{i}\)也一定不超过\(k\),此时\(k\)只能是由\(j>r\)的\(A_{j}-C_{j}\)转移而来)
-
如果我们将第\(i\)个计数器清零,且\(i > r\),那么\(k=A_{i}\)。 (因为\(A_{i}\)>k,将第\(i\)个计数器清零后\(A_{i}-C_{i}=A_{i}>k\),故此时\(k=A_{i}\))
至此,我们发现\(k=\max(A_{i}-C_{i})\),不仅减少状态数,并且转移还是简单的。
我们可以列出DP方程:\(F(k)\)表示状态为\(k\)时的期望操作次数,则\(F(k)=\frac{r}{n} F(k-1) + \frac{1}{n} \sum _{i=r+1}^{n} F(A_{i}) + 1\),\(F(MAX=A_{n})=0\)容易发现,这个转移是有环的。
二、处理转移有环的问题
我首先感觉这题和 https://atcoder.jp/contests/abc189/tasks/abc189_f 是类似的,设\(F(k)=K(k)F(MAX)+B(k)\)不过没继续往下想。
这题题解有一个非常巧妙的“移项去环”的方法:
我们将两侧乘上\(-1\):
两边同时加上\(F(MAX)\):
设\(P(k)=F(MAX)-F(k)\),则:
将\(P(k-1)\)移到左边,\(P(k)\)移到右边:
即:
此时,我们发现,右侧的每个下标都大于左侧,那么这个转移是可行的。
并且最终答案是\(F(MAX)=F(MAX)-F(0)=P(0)\)。
时间复杂度为\(O(MAX)\)或\(O(MAX\log MOD)\),TLE,怎么办?
三、优化
我们发现,对于\(k=a_{i}\)到\(k=a_{i+1}-1\),时,\(r=i\)。(注意我们将下标统一\(+1\)了,现在的\(r\)其实是\(k+1\)时刻的\(r\))那么转移的系数、常数都是相同的。
重设\(P(i)\)为\(k=a_{i}\)时的\(P\)。
设\(\alpha=\frac{n}{i},\beta=-\frac{1}{i} \sum _{j=i+1}^{n} P(A_{j}) + \frac{n}{i}, \theta=P(i+1),\delta=a_{i+1}-a_{i}\),则类似之前的G题,将递推式展开:
前面是一个快速幂,后面是一个等比数列,可以在\(\log MOD+\log MAX\)的时间复杂度内求出。
由于题中保证\(A_{1}=0\),故答案等于\(P(1)\)。
注意\(\delta=0\)的情况\(P(i)=P(i+1)=\theta\)。
总时间复杂度为\(O(n \log MOD+n \log MAX)\)。
代码:
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
using ll=long long;
const int maxn=200005;
const ll mod=998244353;
ll n,sum,a[maxn],P[maxn];
ll qpow(ll x,ll y) {
if(y==0ll) return 1ll;
ll ret=qpow(x,y>>1ll);
ret=ret*ret%mod;
if(y&1ll) ret=ret*x%mod;
return ret;
}
int main() {
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
//P[n]=0 and P[1] is the answer (P[i]=F[a[n]]-F[a[i]])
for(int i=n-1;i>=1;i--) {
ll alpha=n*qpow(i,mod-2ll)%mod;
ll beta=(n-sum+mod)%mod*qpow(i,mod-2ll)%mod;
ll theta=P[i+1];
ll delta=a[i+1]-a[i];
if(delta==0) P[i]=theta;
else P[i]=(theta*qpow(alpha,delta)%mod+(1-qpow(alpha,delta)+mod)%mod*qpow((1-alpha+mod)%mod,mod-2ll)%mod*beta%mod)%mod;
(sum+=P[i])%=mod;
}
printf("%lld\n",P[1]);
return 0;
}
浙公网安备 33010602011771号