由这次的D题从杜老师那学会的比较新奇的背包,我暂且将其命名为因子背包
题意:
一个数
x
x
x被称为好数,当且仅当他被
d
d
d整除而不被
d
2
d^2
d2整除。
给定一个数
n
n
n,问是否有超过两种组成方式,让它成为一个或若干个好数的乘积。
T
T
T组询问,
T
<
=
100
T<=100
T<=100,
n
,
d
<
=
1
0
9
n,d<=10^9
n,d<=109
Solution 1
首先定然可以很暴力的将所有
n
n
n的好数因子给找出来,我们需要的就是用这些数来构成
n
n
n。
由于一个数可以用若干次,所以其实本质上这是一个完全背包问题,我们考虑正常的完全背包我们咋做的。
F
[
i
]
[
j
]
=
F
[
i
−
1
]
[
j
]
+
F
[
i
−
1
]
[
j
/
d
[
i
]
]
F[i][j]=F[i-1][j]+F[i-1][j/d[i]]
F[i][j]=F[i−1][j]+F[i−1][j/d[i]],第一维可以滚掉,也就是
F
[
j
]
+
=
F
[
j
/
d
[
i
]
]
F[j]+=F[j/d[i]]
F[j]+=F[j/d[i]]
问题来了,由于值域太大,我们不可能遍历整个值域来做这个背包,但这时我们发现由于最后能对
F
[
n
]
F[n]
F[n]做出贡献的只有
n
n
n的因子,而对
n
n
n的因子做出贡献的则是
n
n
n的因子的因子(也是
n
n
n的因子),因此我们只需要从小到大遍历
n
n
n的所有因子(最多也就
1300
1300
1300个左右),这样最终贡献给
F
[
n
]
F[n]
F[n]的答案一定是正确的。
现在只剩下一个问题了,虽然我们只需要遍历
n
n
n的所有因子,但是这些因子可能也很大,并不能直接开一个这么大的数组。
我会map!
额,多了个
l
o
g
log
log,这题好像有可能会T。
那咋办?
可以用一个“双指针”的方法,我们开两个数组
i
d
[
1
e
5
+
5
]
,
g
i
d
[
1
e
5
+
5
]
id[1e5+5],gid[1e5+5]
id[1e5+5],gid[1e5+5]
对于每一个因子
c
[
i
]
c[i]
c[i],如果他小于等于
1
e
5
1e5
1e5,那么我们就将
i
d
[
c
[
i
]
]
=
i
id[c[i]]=i
id[c[i]]=i,否则
g
i
d
[
n
/
c
[
i
]
]
=
i
gid[n/c[i]]=i
gid[n/c[i]]=i,这样就可以把值域全部映射回因子个数里,用的时候也判断一下要用的数是通过
i
d
id
id还是
g
i
d
gid
gid存放的即可。
Code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#define ll long long
#define lowbit(x) x&(-x)
#define mp make_pair
#define rep(i,n) for(int i=1;i<=n;i++)
using namespace std;
const int mod=998244353;
const int maxn=1e7+5;
const int maxm=1e5;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=x*10+(c-'0');
c=getchar();
}
return x*f;
}
int T;
int f[2050];
int id[100050],gid[100050];
int tot,c[2050];
signed main()
{
T=read();
while(T--)
{
tot=0;
int n=read(),d=read();
for(int i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(n/i==i) c[++tot]=i;
else c[++tot]=i,c[++tot]=n/i;
}
}
sort(c+1,c+tot+1);
for(int i=1;i<=tot;i++)
{
if(c[i]<=maxm) id[c[i]]=i;
else gid[n/c[i]]=i;
}
f[1]=1;
for(int i=1;i<=tot;i++)
{
if((c[i]%d==0)&&((c[i]/d)%d!=0))
{
for(int j=1;j<=tot;j++)
{
if((n/c[i])%c[j]==0)
{
int x=c[i]*c[j];
int y=(x<=maxm)?id[x]:gid[n/x];
f[y]=min(f[y]+f[j],2);
}
}
}
}
if(f[tot]>=2) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
for(int i=1;i<=tot;i++) f[i]=0;
}
}