题解 P6274 [eJOI2017]六
搬运一下自己的博客
神仙思路。
状压套状压,对于每一个 $N$ 的因数,我们可以求出它带有哪些 $N$ 的质因数(注意,这里不要求质因数次数,因为两个数只要含有同一个质因数就不互质了)。然后用乘法原理优化一下就可以 $O(k2^k)$ 求出含有这些质因数的数的个数。
然后状压,三进制,$2^k-1$ 位,第 $i$ 位的值代表有多少个质因数情况为 $i$ 的。
这样可能不好理解,举个例子:
$N=2^2 \times 3^2 \times 5 \times 7$
所以 $k=4$ ,然后比如像 $84=2^2 \times 3 \times 7$ 很显然是 $N$ 的因数,此时质因数情况为 $1101$
然后记忆化搜索即可。
但是仍然需要优化,首先把 $3$ 进制变成 $4$ 进制更加好写,但是此时需要用两个 $unsigned \ \ long \ \ long$ 存,此时用 $map$ 又多一个 $log$ (虽然没多大事貌似),考虑使用 $hash$ ,就两位,还不用担心取模耗时间( $ull$ ),然后貌似就可以了
细节看代码:
#include<cstdio>
#include<cctype>
#define ull unsigned long long
#define maxn 101
#define maxN 20000002
#define mod 1000000007
inline long long read(){
long long r=0,f=0;
char c;
while(!isdigit(c=getchar()))f|=(c=='-');
while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
return f?-r:r;
}
int k,cs[maxn],cnt[maxn],rel[maxn][maxn];
long long n,m,p[maxn],f[maxN];//f尽量开大,hash冲突概率小一些
inline void add_cnt(long long x){
int zt=0;
for(int i=0;i<k;i++)
zt|=(!(x%p[i])<<i);
cnt[zt]++;
}
inline int val(ull x,ull y,int i){
return (i<32?(x>>(i<<1)):(y>>((i-32)<<1)))&3;
}
inline void add(ull &x,ull &y,int i){
i<32?x+=1ull<<(i<<1):y+=1ull<<((i-32)<<1);
}
long long solve(ull x,ull y){
ull hx=x*131+y;//hash
hx%=maxN;
if(f[hx])return f[hx];
long long ans=1;
for(int i=1;i<(1<<k);i++){
if(val(x,y,i)&2)continue;
ull xc=x,yc=y;
for(int j=1;j<=rel[i][0];j++)
if(val(x,y,rel[i][j])^2)add(xc,yc,rel[i][j]);
//注意,这里如果等于2并不代表无解,因为没有真正加一个数进来
//所以在这一位上超过2没有问题,如果要在这一位真正加一个数会在上面判断
(ans+=1ll*cnt[i]*solve(xc,yc)%mod)%=mod;
}
return f[hx]=ans;
}
int main(){
n=m=read();
for(long long i=2;i*i<=m;i++){//找质因数
if(m%i)continue;
while(!(m%i))m/=i,cs[k]++;
p[k++]=i;
}
if(m>1)cs[k]=1,p[k++]=m;
for(int i=1;i<(1<<k);i++){
for(int j=1;j<(1<<k);j++)//看哪些数与这个数不互质
if(i&j)rel[i][++rel[i][0]]=j;
cnt[i]=1;
for(int j=0;j<k;j++)//乘法原理
if((i>>j)&1)cnt[i]*=cs[j];
}
printf("%lld",(solve(0,0)-1+mod)%mod);//记得减1,因为不能不放数字
return 0;
}

浙公网安备 33010602011771号