题目: 从n到1有两种操作方式 第一种对于x而言 在1到 x中悬着一个数字Y,使得x = x - y,第二种对于x可以在2到n中选择一个数字y ,使得x = x / y.问你从n到1有几种方法
思路: 这个在题解中学到了一种数学数论,就是数论分块, *关于数论分块,就是说F(i)是一个不严格单调的函数,存在x,y属于[L,R],使得F[i] = F[j],这个可以快速的求出来左右边界, 假设左边界是L,那右边界就是 R = n / (n / L),证明的话略,时间复杂度是
n
\sqrt{n}
n 关于这道题可以分为两部分 第一部分是操作一: dp[i] = dp[1] + dp[2] + dp[3] + … dp[i - 1]; 第二部分是操作二: dp[i] = dp[i / 2] + dp[i / 3] + dp[i / 4] + …dp[i / i],这一部分就可以使用数论分块,因为是枚举i,所以左端点 L 固定的话,右端点R = i / (i / L) 所以时间复杂度是O(n *
n
\sqrt{n}
n)
代码:
#include<bits/stdc++.h>#definefifirst#definesesecond#defineendl'\n'#defineall(x) x.begin(),x.end()#definepbpush_back#definePIIpair<int,int>#defineiosios::sync_with_stdio(false); cin.tie(0); cout.tie(0)#defineintlonglongusingnamespace std;constint N =2e5+100,M = N *2,mod =1e9+7,INF =0x3f3f3f3f;int dp[N];voidsolve(){int n,m; cin >> n >> m;
dp[1]=1;int ans =1;for(int i =2;i <= n;i ++){
dp[i]= ans;for(int l =2,r;l <= i;l = r +1){
r = i /(i / l);
dp[i]=(dp[i]+ dp[i / l]*(r - l +1))% m;}
ans =(ans + dp[i])% m;}
cout << dp[n]<< endl;}signedmain(){
ios;int T =1;while(T --)solve();return0;}