版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/82530214
分析
好生优秀的一道题,暴力捞到90分???
来谈谈正解吧
我们很容易发现 n 分解出来的整数因为两两互质,所以他们的因数肯定也是互质的。所以若 n = p1^a1 * p2^a2 * ……*pk*ak,那么其分解出来的每一个整数n= x * y * z ,x要不就是囊括同一种的质因数的全部,要不就不含该种质因数。因为如果 x 含有几个p1,y含有几个p1,那么这两个数肯定不互质。现在我们就得到一个集合U= { p1^a1 ,p2^a2 , …,…,pk*ak } ,然后就来凑那些整数(集合的某个子集,这些子集互不相交且并集为U)。
由于一个 n 最大为1e18,其不同的质因数至多15个(把质数从小往大依次乘起来,乘到第15个质数的时候就超过1e18了),我们就可以状压dp了
特殊考虑一下S集合中出现1的情况,此时不用讨论1,只需要最后在总答案上乘以2即可
不存在分解的情况:n包含的质因数,S集合中不包含
这个状压dp的题就很优秀,并不是一眼就能从数据范围中看出是状压dp,而是需要经过推导,得出一个适合状压dp的数据范围
代码
#include<cstdio>
#include<algorithm>
#define in read()
#define N 100000
#define ll long long
using namespace std;
int k,m,pri[N],cnt=0,temp;
bool mark[N];
int a[N];
pair<int ,int> p[N];
int bit=0;
ll n,f[1<<20];
void prime(){
for(int i=2;i<=N;++i){
if(!mark[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&pri[j]*i<=N;++j){
mark[pri[j]*i]=1;
if(i%pri[j]==0) break;
}
}
}
inline int read()
{
char c=getchar(); int t=0;
while (c<48 || c>57) c=getchar();
while (c>47 && c<58)
t=(t<<1)+(t<<3)+c-48,c=getchar();
return t;
}
bool ok[N];
int v[N],t,num;
void divide(int x){//分解质因数
temp=0;
for(int i=1;i<=cnt&&1ll*pri[i]*pri[i]<=x;++i){
if(x%pri[i]) continue;
p[++temp]=make_pair(0,0);//first-->质因数的大小,second-->这个质因数的个数
p[temp].first=pri[i];
while(x%pri[i]==0){ p[temp].second++;x/=pri[i]; }
if(!ok[pri[i]]) ok[pri[i]]=1,v[++t]=pri[i];//统计共有多少个不同的质因数
}
if(x>1) p[++temp]=make_pair(x,1),v[++t]=x;//最后一次不用判ok(出现与否)因为数组开不了那么大,后面再去重即可
return;
}
bool check(int x){
if(n%x) return 0;
divide(x);
for(int i=1;i<=temp;++i){
ll mul=1;
for(int j=1;j<=p[i].second;++j)
mul*=1ll*p[i].first;
if((n/mul)%p[i].first==0) return 0;//如果这个数x它所含有的单个质因数的个数小于n的,那么它肯定不能纳入考虑范围
}
return 1;
}
bool check_0(){
ll hh=n;
for(int i=1;i<=bit;++i)
while(hh%v[i]==0) hh/=v[i];
if(hh>1) return 1;//如果n还含有除以上那些出现在s集合中的质因数,那么肯定凑不出来
return 0;
}
int cal(int x){//将合法的集合中x这个数,拆成二进制数,第i-1位取1表示v[i]这个质因数在x中出现了
int res=0;
for(int i=1;i<=bit;++i)
if(x%v[i]==0) res|=(1<<i-1);
return res;
}
int main()
{
prime();
scanf("%lld",&n);k=in;
int i,j,x,pow=1;
for(i=1;i<=k;++i){
x=in;
if(x==1) {pow=2;continue;}
if(check(x)) a[++num]=x;//num-->m,t-->num
}
sort(v+1,v+t+1);
for(i=1;i<=t;++i)
if(v[i]!=v[i+1]) v[++bit]=v[i];
if(check_0()) {
printf("0");
return 0;
}
for(i=1;i<=num;++i)
a[i]=cal(a[i]);
f[0]=1;
for(i=0;i<(1<<bit);++i)//枚举所有状态
for(j=1;j<=num;++j)//枚举所有可以加的数
if((i&a[j])==0&&i<a[j])//如果这个状态和当前这个数所含有的质因数不冲突,并且我们强制规定从小往大加
f[i|a[j]]+=f[i];
printf("%lld",f[(1<<bit)-1]*pow);
return 0;
}