题意:给出一个数组a,然后可以移除一些元素变成数组b,这个数组b有个规定是:如果第i个元素可以整除i,那么这个数组b就是好的,现在问数组a可以构造出多少个数组b
样例解释一:
5
2 2 1 22 14
可以构成
{2}, {2,2} {2,22}, {2,14}, {2}, {2,22}, {2,14}, {1}, {1,22}, {1,14}, {22}, {22,14}, {14}
所以答案是13
题解:肯定不能暴力啦,不能还能怎么,dp走走,可以想到这句话,如果第i个元素可以整除i,显然就这点就可以状态转移么,怎么进行状态转移呢?首先用dp[i][j]表示a数组的第i个元素下组成b数组(其中元素有j个)的方案数,然后写出状态转移方程即可,对于j是1的情况,dp[i][j]=dp[i-1][j]+1,意思就是此时这个位不就可以直接再算一个么,对于其他能整除的情况,也就是存在此时这个元素的因数j时,直接dp[i][j]=dp[i-1][j]+dp[i-1][j-1],意思不就是前一位上所有的组成的b数组(其中元素有j个)的方案数+前一位上所有的组成的b数组(其中元素有j-1个)的方案数<这种直接在后面放上这位数即可>,再看看数组范围肯定得用滚动dp,滚动dp就得仔细考虑方向了,逆着来就行了,这样直接交上去T在第8组,然后想想怎么进行优化一波呢?如果能直接知道每个数的因数是谁就好了,想起因数欧拉函数就跟着想起来了,然后试着把欧拉函数改了改,在欧拉乘法的中间改成压入,具体看代码,挺好懂的。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+50;
const int mod=1e9+7;
int a[maxn],dp[maxn];
bool used[maxn];
vector<int>v[maxn];
void maketable()
{
for(int i=1;i<maxn;i++){
if(!used[i]){
for(int j=i;j<maxn;j+=i){
v[j].push_back(i);
}
used[i]=true;
}
}
}
int main()
{
maketable();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
for(int j=v[a[i]].size()-1;j>=0;j--){
if(v[a[i]][j]<=i){
if(v[a[i]][j]==1){
dp[1]=(dp[1]+1)%mod;
}else{
dp[v[a[i]][j]]=(dp[v[a[i]][j]-1]+dp[v[a[i]][j]])%mod;
}
}
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans=(ans+dp[i])%mod;
}
printf("%d\n",ans);
return 0;
}