题意
1e4次询问,给定n与k,问是否能将n分为若干个k的因数之和。
注意由于题面原因,k=1时答案为NO
n<=1e18,k<=1e15,最多50个不同的k.
题解
- 先分解质因数k=p1p2p3…
- 显然只需要用质因子拼出n就行
- 然后相当于求 是否存在自然数解{a1,a2,a3…}
根据超哥的讲义
转换为图论问题,记其中最小质因子为p1.
则建有p1个节点的图,分别代表模p1意义下的0..p1-1
对于每一个节点x与质因子pi,向(pi+x)%p1连一条边权为pi的边。
这样从0开始跑到x的最短路就相当于求出让
的最小值。
对于一个询问n,k,若在k的图中dis[n%p1]<=n,则可以加若干个p1得到n.因此是yes,否则是no.
还有一个问题,就是点数可能过多。发现对于只有一个或两个质因子的k只需要简单判一下就可以得出答案,这样点数就被限制到1e5之内了
其核心就是把问题转化在modp1意义下求解。而p1是1e5范围内的,这样可以将整个问题限制在1e5范围内,再通过p1无限使用来达到降低复杂度的目的。
两个质因子具体怎么判:
对于k=ab,若ax+by=n,则有y=n/b(mod a),又因为a,b互质,所以可以求出mod a意义下的y.
我们希望x,y都是非负整数,所以y越小越好,即取mod a意义下的y就行
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
typedef long double db;
int t;
map<int,int> has;
ll fj[55][50],gs[55],bh;
ll n,k;
const ll S=31624000;
const ll mf=1e5+10;
bool is[S];
ll p[2000000];
void init() {
for (int i=2; i<S; i++) {
if (!is[i]) p[++p[0]]=i;
for (int j=1; j<=p[0] && p[j]*i<S; j++){
is[i*p[j]]=1;
if (i%p[j]==0) break;
}
}
}
ll mx,dis[mf];
vector<pair<int,int> > e[mf];
vector<int> ans[51];
ll Q[mf*100],vis[mf];
void build(ll z) {
mx=fj[z][1];
for (int i=0; i<mx; i++) {
e[i].clear();
for(int j=1; j<=gs[z]; j++)
e[i].push_back(make_pair((i+fj[z][j])%mx,fj[z][j]));
}
memset(dis,127,sizeof dis); dis[0]=0;
int L=0,R=1; Q[R]=0;
while (L<R) {
int x=Q[++L]; vis[x]=0;
for (int i=0; i<e[x].size(); i++) {
ll y=e[x][i].first,c=e[x][i].second;
if (dis[x]+c<dis[y]) {
dis[y]=dis[x]+c;
if (!vis[y]) {
vis[y]=1; Q[++R]=y;
}
}
}
}
for (int i=0; i<mx; i++) ans[z].push_back(dis[i]);
}
int fen(ll x) {
bh++;
for (int i=1; p[i]*p[i]<=x; i++) {
if (x%p[i]==0) {
fj[bh][++gs[bh]]=p[i];
while (x%p[i]==0) x/=p[i];
}
}
if (x!=1)
fj[bh][++gs[bh]]=x;
if (gs[bh]>=3) build(bh);
return bh;
}
void getans(int n,int z) {
if (n<fj[z][1]) printf("NO\n"); else
if (ans[z][n%fj[z][1]]<=n) printf("YES\n");
else printf("NO\n");
}
ll ksm(ll x,ll y,ll mo) {
ll ret=1; for (; y; y>>=1) {
if (y&1) ret=ret*x%mo;
x=x*x%mo;
}
return ret;
}
int main() {
init();
// freopen("F.in","r",stdin);
// freopen("F.out","w",stdout);
for (cin>>t; t; t--) {
scanf("%I64d %I64d",&n,&k);
int &z=has[k];
if (!z) z=fen(k);
if (k==1) {
printf("NO\n");
continue;
}
if (gs[z]==1) {
printf(n%fj[z][1]?"NO\n":"YES\n");
} else if (gs[z]==2) {
ll a=fj[z][1],b=fj[z][2];
ll my=ksm(b,a-2,a) * (n%a) % a;
if (my*b <= n) printf("YES\n"); else printf("NO\n");
} else getans(n,z);
}
}