链接:https://ac.nowcoder.com/acm/problem/21738
来源:牛客网
题目描述
牛牛喜欢这样的数组:
1:长度为n
2:每一个数都在1到k之间
3:对于任意连续的两个数A,B,A<=B 与(A % B != 0) 两个条件至少成立一个
请问一共有多少满足条件的数组,对1e9+7取模
输入描述:
输入两个整数n,k
1 ≤ n ≤ 10
1 ≤ k ≤ 100000
输出描述:
输出一个整数
题目分析:动态规划问题,第一步设状态,第二步初始值,第三步状态转移,第四步确定答案。
1.状态:f[i][j]表示在前i个数中,第i个数选j能当前满足条件的数列。
2.初始值:f[1][i]=1 (1<=i<=k)
3.状态转移方程:f[i][j]=(sum(f[i-1][x])+sum(f[i-1][y]))%M (1<=x<=j,j+1<=y<=k&&y%j!=0)
4.Ans=sum(f[n][i]) (1<=i<=k)
时间复杂度:O(n*k^2)
但是,k<=100000
很明显,会超时
因此,必须优化
观察状态转移方程可以发现,1到j和j+1到k刚好k次,且都是连续相加求和,
所以设s1,s2数组分别表示f[i-1][j]前缀和和后缀和。
状态转移方程为:f[i][j]=sum(s1[j]+s2[j])%M-f[i-1][x] (x%j==0)
Ans=sum(f[n][i])
代码如下:
#include<bits/stdc++.h> #define M 1000000007 using namespace std; int n,k; long long ans,f[12][100002],s1[100002],s2[100002]; int main() { cin>>n>>k; for(int i=1;i<=k;i++)f[1][i]=1; for(int i=2;i<=n;i++) { memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2)); for(int j=1;j<=k;j++)s1[j]=(s1[j-1]+f[i-1][j])%M; for(int j=k;j>=1;j--)s2[j]=(s2[j+1]+f[i-1][j])%M; for(int j=1;j<=k;j++) { f[i][j]=(s1[j]+s2[j])%M; for(int x=j;x<=k;x+=j)f[i][j]=(f[i][j]+M-f[i-1][x])%M; // for(int x=1;x<=j;x++) // f[i][j]+=f[i-1][x]; // if(f[i][j]>=1e15)f[i][j]%=M; // for(int x=j+1;x<=k;x++) // if(x%j!=0)f[i][j]+=f[i-1][j]; // if(f[i][j]>=1e15)f[i][j]%=M; } } for(int i=1;i<=k;i++) ans=(ans+f[n][i])%M; cout<<ans%M; return 0; }欢迎大家讨论,谢谢!