题意:
给你一个长度为n的数组,对于数组的每个位置x,你可以执行两种操作:①.从1~x-1(包括1和x-1)中选一个数字y从x转移到y。②.从2 ~ x(包括2和x)选一个数字y从x转移到x/y(向下取整)这个位置。要求从位置n到达位置1的方案数有多少种。这里特别需要注意方案数的算法:假设当前在4号位置,通过操作①直接选择1号位置是一种方案,通过操作②选则4/4的位置进行转移也算一种方案,这两种情况算两种方案。
tle思路:
用dp的思想,首先设dp[n]=1,然后从n~1进行循环,设当前位置为i,首先把能通过操作①能到达的所有位置都加上dp[i],也就是dp[1 ~ i]+=dp[i].然后把能通过操作②到达的点都加上dp[i],也就是dp[i/(2 ~ i-1)]+=dp[i].复杂度为O(N^2),会超时。
代码示例:
#include<bits/stdc++.h>
using namespace std;
int dp[2*100005];
int main()
{
int n,mod;
cin>>n>>mod;
dp[n]=1;
for(int i=n;i>0;i--)
{
for(int j=1;j<i;j++)//i节点通过操作1能到达的节点j
dp[j]+=dp[i],dp[j]%=mod;
for(int j=2;j<=i;j++)//i节点通过操作2能到达的节点i/j(向下取整)
{
dp[i/j]+=dp[i];
dp[i/j]%=mod;
}
}
cout<<dp[1]<<endl;
return 0;
}
ac思路:
需要将上面的两种操作进行优化,设当前位置为i,操作1:对于从i位置能通过操作①到达的位置为1~i-1这些节点的,也就是dp[1]+=dp[i],dp[2]+=dp[i]…dp[i-1]+=dp[i],对于下一次循环就是dp[1]+=dp[i-1],dp[2]+=dp[i-1],…,dp[i-2]+=dp[i-1],这里是不是能发现一个规律,对于任意一个位置x,他能从(x+1 ~n)这些地方通过操作1到达,也就是dp[x]+=dp[x+1]+dp[x+2]+…+dp[n],因为我们是从后往前遍历,那么那么我们只需要用一个sum记录当前i位置后面dp的和是多少,加到当前的dp[i]上就好了,也就是dp[i]+=sum,这样对于每个位置i就不需要通过O(n)来更新他能到达的位置了。操作2:操作2从2 ~ i(包括2和i)选一个数字j从i转移到i/j(向下取整)这个位置。上面tle的方法是从2 ~ i遍历j,然后让dp[i/j]+=dp[i],因为i/j这个数字可能重复出现多次(比如10/4=2,10/5=2),也就是对同一个dp[i/j]的值操作了多次,要尽量减少这样的重复操作。优化方法:因为通过操作②从位置i到位置y的方法数为i/y-i/(y+1),可以直接求得方法数。既然可以直接知道方法数,就可以直接针对每个dp[y]+=(i/y-i/(y+1))*dp[i]即可。i/j肯定有1 ~sqrt(i)这些数字,而sqrt(i) ~ i不一定能通过i/j获得,所以对于j从2 ~ sqrt(i)我们用遍历j从2~ sqrt(i),**dp[i/j]+=dp[i]**获得,对与后面的i/j为1 ~ sqrt(i)的我们使用y从1~sqrt(n) dp[y]+=[i/y-i/(y+1)]× dp [i]获得。这里需要注意如果i/sqrt(i)==sqrt(i),会让dp[sqrt(i)]多加了一次dp[i],这种情况下要提前对dp[sqrt(i)] - =dp[i].
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[2*100005];
int main() {
ll n,mod;
cin>>n>>mod;
dp[n]=1;
ll sum=0;
for(int i=n; i>=1; i--) {
dp[i]+=sum;//位置(i+1~n)有sum种方法通过操作1到达当前位置i
sum+=dp[i];//更新后的sum为 位置(i~n)有sum种方法通过操作1到i-1
sum%=mod;
dp[i]%=mod;
ll m=(ll)sqrt(i);
if(i>=2) {//i==1的时候操作2无法操作
if(i/m==m)//dp[sqrt(i)]会多加一次,所以提前剪掉
dp[m]-=dp[i];
for(int j=1; j<=m; j++) {
if(j!=1)//j从2~sqrt(i)每个dp[i/j]+=dp[i]
dp[i/j]+=dp[i];
dp[i/j]%=mod;
int l=i/j;
int r=i/(j+1);
if(l>r&&i/j!=1)
dp[j]+=(l-r)*dp[i];//y 从1~sqrt(i) 每个dp[y]+=[i/y-i/(y+1)]*dp[i]
dp[j]%=mod;
}
}
}
cout<<dp[1]<<endl;
return 0;
}