CF986F(round 485 div1) Oppa Funcan Style Remastered (数学->图论)

题意

1e4次询问,给定n与k,问是否能将n分为若干个k的因数之和。
注意由于题面原因,k=1时答案为NO
n<=1e18,k<=1e15,最多50个不同的k.

题解

  • 先分解质因数k=p1p2p3…
  • 显然只需要用质因子拼出n就行
  • 然后相当于求 a 1 p 1 + a 2 p 2 + a 3 p 3.. = n 是否存在自然数解{a1,a2,a3…}

根据超哥的讲义

转换为图论问题,记其中最小质因子为p1.
则建有p1个节点的图,分别代表模p1意义下的0..p1-1
对于每一个节点x与质因子pi,向(pi+x)%p1连一条边权为pi的边。
这样从0开始跑到x的最短路就相当于求出让 a 1 p 1 + a 2 p 2 + a 3 p 3.. m o d p 1 = 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);
    }
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/80841116