Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))
D1. Up the Strip (simplified version)
D2. Up the Strip
题意:
给出一个数,每次有两种操作,设
为当前数字
- 选择
- 选择(向下取整) 
问从变为
的方案数。
中
中
思路:
- 假设表示从 变到 的方案数,那么 
 前一项表示通过减法到达,后一项表示通过除法到达 。 
 这样的复杂度是的,考虑能够怎么优化。 
- 可以用前缀和优化,每次加上 - 就行。 
 后面一项,由于每次都是向下取整,真正的除数只有- 个,可以用整除分块来做,每次维护因子能够影响到的区间。时间复杂度 
 学习链接
for(int l=2,r;l<=i;l=r+1){
r=i/(i/l);
dp[i]=(dp[i]+(r-l+1)*dp[i/l]%mod)%mod;
}
- 这个复杂度过显然不大行,考虑进一步的优化。 
 看看是否能从后继状态转移过来,比如当枚举到时,除数为 ,那么 就可以通过除 到达 。 
 进一步的扩展,对于的每个除数 ,都能够通过 中的数除 到达 。 
 可以枚举倍数,并且维护后缀和。时间复杂度
for(int j=2;j*i<=n;j++){//(i*j)/j=i,所以i*j可以转移到i
int r=min(n,(ll)i*j+j-1);//右边界
dp[i]=(dp[i]+sum[i*j]-sum[r+1]+mod)%mod;//后缀和
}
代码:
const int maxn=2e5+10;
ll n,dp[maxn],mod,sum[maxn];
int main(){
n=read,mod=read;
dp[1]=1;dp[2]=2;
sum[1]=1,sum[2]=3;
rep(i,3,n){
dp[i]=(dp[i]+sum[i-1])%mod;
for(int l=2,r;l<=i;l=r+1){
r=i/(i/l);
dp[i]=(dp[i]+(r-l+1)*dp[i/l]%mod)%mod;
//cout<<l<<" "<<r<<" "<<dp[i]<<endl;
}
sum[i]=(sum[i-1]+dp[i])%mod;
}
/*rep(i,1,n){
cout<<i<<" "<<dp[i]<<endl;
}*/
printf("%lld\n",dp[n]);
return 0;
}
const int maxn=4e6+10;
ll n,dp[maxn],mod,sum[maxn];
int main(){
n=read,mod=read;
dp[n]=sum[n]=1;
per(i,n-1,1){
dp[i]=sum[i+1];
//sum[i]=(sum[i+1]+dp[i])%mod;
for(int j=2;j*i<=n;j++){
int r=min(n,(ll)i*j+j-1);
dp[i]=(dp[i]+sum[i*j]-sum[r+1]+mod)%mod;
}
sum[i]=(sum[i+1]+dp[i])%mod;
}
write(dp[1]);
return 0;
}
参考 昨晚比赛结束了看群里的讨论,两个题本质是的转移方式

 好有道理~
                










