链接:
https://codeforces.com/problemset/problem/1561/D1
题意:
一开始为n,把n变成1,可以做两种操作(无数次)
对于位置x的数字:
1、减【1,x-1】任意一个数
2、除与【2,x】任意一个数(向下取整)
求有多少种变法
输入
3 998244353
输出量
5
输入
5 998244353
输出量
25
输入
42 998244353
输出量
793019428
解:
这一波属于是1900大佬提点了,一开始就想到了DP,但是除法这部分完全不会写
因为x的数字可以减【1,x-1】任意一个数字,所以x可以变成【1,x-1】任何一个数字
所以减法反面,dp[i]首先要继承i+1到n所有dp的和,因为前面任意一种都可以通过减法达成i;
然后是除法部分,只能说是妙哇
正常要把dp[i]放入每一个i/2,i/3,…,i/i;
但是!!!
由于向下取整,所以导致i除某些数字结果是同一个数,比如3/2=1,3/3=1;
所以在一个区间的dp[i/(l~r)]其实全部属于dp[i/p]!
上面的例子表示l=2,r=3的区间都属于dp[1],所以r-l+1*dp[3]加入dp[1]即可
而选取右端点的方法就是i/(i/l)!!!,l为左端点
实际代码:
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define csh(a) memset(a,0,sizeof(a))
using namespace std;
typedef long long int ll;
const int N=4E6+10;
ll mod;
ll sum=0;
ll ans;
ll dp[N];
int main()
{
ll n,m;
cin>>n>>m;
dp[n]=1;
mod=m;ans=0;
for(ll i=n;i>=2;i--)
{
dp[i]=(dp[i]+sum)%mod;//继承部分 减法部分
for(int l=2,r=i/(i/l);l<=i;l=r+1)
{
r=i/(i/l);//右端点确定
dp[i/l]=(dp[i/l]+(r-l+1)*dp[i])%mod;//继承部分 除法部分
}
sum=(sum+dp[i])%mod;//把i和前面的方法数和记录,留给i-1;
}
cout<<(dp[1]+sum+mod)%mod<<endl;
return 0;
}
限制:
time limit per test
6 seconds
memory limit per test
128 megabytes
input
standard input
output
standard output