题目链接
https://codeforces.com/contest/1561/problem/D2
题意
n阶台阶下楼梯,可以一次走多步,可以一次从当前格子x走到x/z(下取整)格子。z可在1到x-1选择。问走到1的方案数。
思路
考虑dp
第一种操作很容易处理。dp[i]可以直接由sum[i-1]转移过来。
第二种操作其实也简单,我们就枚举z,然后从x/z转移不得了。
但是这样的复杂度是n平方的,这时要用到一些优化。
考虑整除分块,一个数的约数显然只有根号级别,也就是说我们枚举z的时候有很多是重复的,那么我们不如枚举x/z的结果,套一个整除分块板子,复杂度是n根号n,可以通过。
再考虑D2。
我们考虑倒推,将dp数组意义改为走到i位置的方案数,那么初态dp[n]=1,答案是dp[1].一个状态dp[i]的转移就是由i+1到n的后缀和作为第一种操作,第二种操作,则是枚举所有j,让j是i的倍数,之后计算这个倍数的贡献。比如当前是x,那么我们有2x,2x+1两个位置计算的是dp[2x]的转移,3x,3x+1,3x+2三个位置计算的是dp[3x]的转移,以此类推。容易发现复杂度是调和级数。
代码
D1 数组叫bit是因为刚开始脑子不清楚,套了个树状数组
void solve(){
cin>>n>>mod;
bit[1]=1;
int sum=1;
for(int i=2;i<=n;i++){
int t=sum;
for(int l=2,r;l<=i;l=r+1){
r=i/(i/l);
t=(t+bit[i/l]*((r-l+1ll)%mod))%mod;
}
bit[i]=t;
sum=(sum+t)%mod;
}
cout<<bit[n]<<endl;
}
void solve(){
cin>>n>>mod;
bit[n]=1;
sum[n]=1;
for(int i=n-1;i;i--){
bit[i]=sum[i+1]%mod;
for(int j=2;j*i<=n;j++){
int l=i*j,r=min(n,i*j+j-1);
bit[i]=(bit[i]+sum[l]-sum[r+1]+mod)%mod;
}
sum[i]=(bit[i]+sum[i+1])%mod;
}
cout<<bit[1]<<endl;
}