2020/10/27 模拟赛 数列
Description
小 s 的毒瘤数学老师布置了一个毒瘤问题,于是他去询问小 r。 小 s 给了小 r 一个正整数 $N$,并且保证$N$ 的不同质因子个数不超过 $6$。 现在从一个空数列开始,把$N$ 的因数一个一个加到数列中,每次加入的数可能重复,但必须都大 于$1$ 。 每次添加新的数字的时候,需要保证这个数 $x$与数列中已有的数至多一个有大于$1$ 的公因数。 例如,当$N$等于 12156144 的时候, (42),(66,7,7,23,299,66),(42,12156144) 是合法的, 而数列 (5,11) 是不合法的,因为 5 不是 12156144 的因数,数列 (66,13,143) 也是不合法的,因为 143 与其他两个数有大于 1 的公因数。 小 s 想要知道,共有多少个不同的数列满足以上条件。 特别地,两个数列不同,当且仅当他们的长度不同或某一处的数不同。
Solution
考虑逐位确定数列,因为在数列中每个质因数最多出现2次,发现答案只与当前选择到第几位和之前出现次数为1的因数的划分方式有关
记忆化搜索,总状态数为$\sum_{i=0}^6 Bell_i\binom{6}{i}2^{6-i} $
#include<iostream> #include<cstring> #include<cstdio> #include<map> using namespace std; long long n,temp,cnt=-1,prime[10],pc[10],num[70],col[10]; const long long mod=1e9+7; map<long long,long long>mp[15]; inline long long read() { long long f=1,w=0; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return f*w; } bool check(long long s,long long t) { long long tot=0; memset(col,0,sizeof(col)); for(long long i=0;i<=cnt;i++) { if(t>>i&1) { long long v=s>>3*i&7; if(v==7) { return false; } if(v&&!col[v]) { col[v]=++tot; } if(tot>1) { return false; } } } return true; } long long trans(long long s,long long t) { long long r=0,tot=0; for(long long i=0;i<=cnt;i++) { long long v=s>>3*i&7; if(v<7) { tot=max(tot,v); } } for(long long i=0;i<=cnt;i++) { long long v=s>>3*i&7; if(v==7) { r|=7<<3*i; } else { if(v) { if(t>>i&1) { r|=7<<3*i; } else { r|=v<<3*i; } } else { if(t>>i&1) { r|=tot+1<<3*i; } } } } s=r; tot=0; r=0; memset(col,0,sizeof(col)); for(long long i=0;i<=cnt;i++) { long long v=s>>3*i&7; if(v==7) { r|=7<<3*i; } else if(v) { if(!col[v]) { col[v]=++tot; } r|=col[v]<<3*i; } } return r; } long long dfs(long long k,long long s) {//cout<<k<<" "<<s<<endl; if(mp[k][s]) { return mp[k][s]; } long long ret=!!k; for(long long i=1;i<1<<(cnt+1);i++) { if(check(s,i)) { (ret+=1ll*dfs(k+1,trans(s,i))*num[i]%mod)%=mod; } } return mp[k][s]=ret; } int main() { n=read(); temp=n; for(long long i=2;i*i<=temp;i++) { if(!(temp%i)) { prime[++cnt]=i; while(!(temp%i)) { temp/=i; ++pc[cnt]; } } } if(temp>1) { prime[++cnt]=temp; pc[cnt]=1; } for(long long i=1;i<1<<(cnt+1);i++) { num[i]=1; for(long long j=0;j<=cnt;j++) { if(i>>j&1) { num[i]*=pc[j]; } } } printf("%d\n",dfs(0,0)); return 0; }