【CF 1061C|GDFZOJO 3505】Multiplicity
作者:互联网
传送门
题意简述
从序列\({a1,a2,\dots,an}(1≤ai≤1000000)\)中选出非空子序列\({b1,b2,\dots,bk}\),一个子序列合法需要满足 ∀ i∈[1, k],i∣bi∀ i∈[1, k],i∣bi 。求有多少互不相等的合法子序列,答案对\(10^9+7\)取模。
序列\(\{1,1\}\)有\(2\)种选法得到子序列\(\{1\}\),但\(1\)的来源不同,认为这两个子序列不相等。
解析
这题的复杂度要求我们需要使用线性作法(废话)。
考虑dp,如果\(i|b[i]\),则它是一种方案,反之亦然。
所以有这么一个方程:
\[f_{i,j} = \left\{ \begin{array}{lr} f_{i,j} & , j\not|a_i\\ f_{i,j}+f_{i-1,j-1} & , j|a_i \end{array} \right. \]
该dp的时间复杂度为\(O(n\max(a_i))\)的,显然过不去,需要优化。
我们发现能取到第二条转移方程当且仅当\(j\)是\(a_i\)的因数,而第一种第一种转移方程可以使用滚动数组直接省略掉。于是我们直接枚举\(a_i\)的因数,只在因数位置进行转移。同时注意因为\(i\)相同时的\(f\)时不能互相影响,所以对因数的转移要从大到小进行。好像01背包呀QAQ。
于是就可以轻松通过本题了。
Code:
#include<bits/stdc++.h>
namespace Bu_Yao_Tian_Tian_Bi_Sai {
using namespace std;
#define LL long long
inline LL read() {
char c=getchar();
LL sum=0;
while(c<'0'||c>'9') {
c=getchar();
}
while(c>='0'&&c<='9') {
sum=(sum<<3)-'0'+(sum<<1)+c,c=getchar();
}
return sum;
}
inline void write(LL x) {
if(x>9) {
write(x/10);
}
putchar(x%10+'0');
}
const int d[4][2]= {
{1,0},{0,1},{-1,0},{0,-1}
},mod=1e9+7,N=1e5+5;
}
using namespace Bu_Yao_Tian_Tian_Bi_Sai;
int a[N],dp[N];
vector<int> v[N];
inline void chai(int x) {
stack<int> sk;
int tmp=a[x];
for(int i=1; i*i<=tmp; i++) {
if(tmp%i) {
continue;
}
v[x].push_back(i);
if(i*i^tmp) {
sk.push(tmp/i);
}
}
while(sk.size()) {
v[x].push_back(sk.top()),sk.pop();
}
}
int main() {
int n=read();
for(int i=1; i<=n; i++) {
a[i]=read(),chai(i);
}
dp[0]=1;
for(int i=1; i<=n; i++) {
for(int j=v[i].size()-1; j>=0; j--) {
int tmp=v[i][j];
if(tmp<=n&&dp[tmp-1]) {
dp[tmp]=(dp[tmp]+dp[tmp-1])%mod;
}
}
}
int ans=0;
for(int i=1; i<=n; i++) {
ans=(ans+dp[i])%mod;
}
write(ans);
return 0;
}
/*
_ __
(_)/ \
<'_, ____)~~~~~~~
^^ ^^
*/
完美撒花~
标签:tmp,1061C,int,Multiplicity,CF,Tian,因数,序列,dp 来源: https://www.cnblogs.com/Sam2007/p/12782525.html