CF986F Oppa Funcan Style Remastered

CF986F

给定 nnkk,问是否能将 nn 分为若干个 kk 的非 11 因数之和,每个因数都可以被选多次。

一共 TT 组询问。

对于每组询问,若满足输出 Yes,否则输出 No

对于 100%100\% 的数据,保证 1T104,1n1018,1k10151 \leq T \leq 10^4,1 \leq n \leq 10^{18},1 \leq k \leq 10^{15} 且最多有 5050 种不同的 kk

sol

首先发现,若 nn 能被表示成若干个 kk 的非 11 因数之和,则 nn 也一定能被表示成若干个 kk 的质因子之和。

证明:每个 kk 的非质因子一定能被表示成 pqpq 的形式,其中 pp 为质数,则将该非质因子拆成 qqpp 相加即可。

然后考虑将kk 质因子分解,设 k=i=1dpiqik=\prod_{i=1}^{d}p_i^{q_i},其中 pip_i 为质数,qiq_i 为自然数。

由于不同的 kk 最多只有 5050 个,所以可以考虑筛完质数后枚举分解,由于 107.510^{7.5} 的质数只有约 107.5ln107.5\frac{10^{7.5}}{\ln 10^{7.5}} 个,所以这部分总时间复杂度大约在 1.2×1081.2\times 10^{8} 级别,可过。(当然也可以用 Pollard-Rho 分解)

再对 dd 分类讨论:

  • d=0d=0:显然,k=1k=1,无解。
  • d=1d=1:显然,kPk \in \mathbb{P},直接判断 [kn][k|n] 即可。
  • d=2d=2:设 k=xuyvk=x^u y^v,那么题意转换为判断是否 a,b[0,]Z,ax+by=n\exists a,b \in [0,\infty] \cap \mathbb{Z},ax+by=n,假设有解,显然有 bb 的最小值 bmin=n×y1modxb_{min}=n\times y^{-1} \bmod x,最后判断 [y×bminn][y \times b_{min} \leq n] 即可。
  • d3d \geq 3:根据上面的思路,题意转换为判断是否 x1,x2,,xm[0,]Z,i=1dpixi=n\exists x_1,x_2,\cdots,x_m \in [0, \infty] \cap \mathbb{Z},\sum_{i=1}\limits^{d}p_ix_i=n,直接检验貌似有点棘手,不过不难发现有一个性质,那就是对于任意 xix_i 如果 nn 可行,那么 n+zpin+zp_i 也可行,发现这类似于同余。

于是引入一个算法:同余最短路。

考虑令 disidis_i 表示能被表示出来的满足 umodp1=iu \bmod p_1=i 的最小的 uu

于是对于一个询问 nn,只需判断 [disnmodp1n][dis_{n \bmod p_1} \leq n] 即可。

那么怎么计算 disdis 呢?

转到图上,对于所有 2id,0j<p12 \leq i \leq d,0 \leq j < p_1,连边 j(j+pi)modp1j \to (j+p_i)\bmod p_1,权值为 pip_i,从 00 开始使用最短路算法即可得到 disdis

可以发现,d13d \leq 13,边数是 O(dk3)\mathcal O(d\sqrt[3]{k}) 的,点数是 O(k3)\mathcal O(\sqrt[3]{k}) 的,可过。

总时间复杂度为 O(Tlogk+50×dk3logk)\mathcal O(T \log k+50\times d \sqrt[3]{k}\log k),可过。

最优解

#include <bits/stdc++.h>
using namespace std;

namespace PR
{
	typedef long long ll;
#define lll __int128

	template <class kkk>
	inline kkk qr(kkk sample)
	{
		kkk ret = 0, sgn = 1;
		char cur = getchar();
		while (!isdigit(cur))
			sgn = (cur == '-' ? -1 : 1), cur = getchar();
		while (isdigit(cur))
			ret = (ret << 1) + (ret << 3) + cur - '0', cur = getchar();
		return sgn == -1 ? -ret : ret;
	}

	ll max_factor;

	inline ll gcd(ll a, ll b)
	{
		if (b == 0)
			return a;
		return gcd(b, a % b);
	}

	inline ll qp(ll x, ll p, ll mod)
	{
		ll ans = 1;
		while (p)
		{
			if (p & 1)
				ans = (lll)ans * x % mod;
			x = (lll)x * x % mod;
			p >>= 1;
		}
		return ans;
	}

	inline bool mr(ll x, ll b)
	{
		ll k = x - 1;
		while (k)
		{
			ll cur = qp(b, k, x);
			if (cur != 1 && cur != x - 1)
				return false;
			if ((k & 1) == 1 || cur == x - 1)
				return true;
			k >>= 1;
		}
		return true;
	}

	inline bool prime(ll x)
	{
		if (x == 46856248255981ll || x < 2)
			return false;
		if (x == 2 || x == 3 || x == 7 || x == 61 || x == 24251)
			return true;
		return mr(x, 2) && mr(x, 61);
	}

	inline ll f(ll x, ll c, ll n)
	{
		return ((lll)x * x + c) % n;
	}

	inline ll PR(ll x)
	{
		ll s = 0, t = 0, c = 1ll * rand() % (x - 1) + 1;
		int stp = 0, goal = 1;
		ll val = 1;
		for (goal = 1;; goal <<= 1, s = t, val = 1)
		{
			for (stp = 1; stp <= goal; ++stp)
			{
				t = f(t, c, x);
				val = (lll)val * abs(t - s) % x;
				if ((stp % 127) == 0)
				{
					ll d = gcd(val, x);
					if (d > 1)
						return d;
				}
			}
			ll d = gcd(val, x);
			if (d > 1)
				return d;
		}
	}

	inline void fac(ll x, set<ll> &st)
	{
		if (x <= max_factor || x < 2)
			return;
		if (prime(x))
		{
			st.insert(x);
			return;
		}
		ll p = x;
		while (p >= x)
			p = PR(x);
		while ((x % p) == 0)
			x /= p;
		fac(x, st), fac(p, st);
	}

	inline set<ll> get(ll x)
	{
		set<ll> st;
		fac(x, st);
		return st;
	}
}

using namespace PR;

inline ll read()
{
	ll 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;
}

const int _ = 1e7 + 7;

int T, ans[10007];

ll n, k, dis[_];

map<ll, vector<pair<ll, int> > > mp;

inline ll qpow(ll x, ll y, ll mod)
{
	ll res = 1;
	while(y)
	{
		if(y & 1) res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

signed main()
{
	T = read();
	for(int i = 1; i <= T; ++i)
	{
		ll x = read();
		mp[read()].push_back({x, i});
	}
	for(auto t : mp)
	{
		ll k = t.first;
		if(k == 1) continue;
		auto d = get(k);
		if(d.size() == 1)
		{
			for(auto i : t.second) ans[i.second] = !(i.first % k);
			continue;
		}
		if(d.size() == 2)
		{
			ll x = *d.begin(), y = *d.rbegin();
			for(auto i : t.second)
			{
				ll c = i.first, id = i.second;
				if(c % x == 0)
				{
					ans[id] = 1;
					continue;
				}
				ll b = c % x * qpow(y, x - 2, x) % x;
				ans[id] = b * y <= c;
			}
			continue;
		}
		vector<ll> f(d.begin(), d.end());
		int x = f[0];
		f.erase(f.begin());
		for(int i = 0; i < x; ++i) dis[i] = -1;
		dis[0] = 0;
		priority_queue<pair<ll, int>> q;
		q.push({0, 0});
		while(!q.empty())
		{
			auto now = q.top();
			q.pop();
			int p = now.second;
			if(now.first != -dis[p]) continue;
			for(auto i : f)
			{
				int tmp = (p + i) % x;
				if(dis[tmp] == -1 || dis[tmp] > dis[p] + i)
				{
					dis[tmp] = dis[p] + i;
					q.push({-dis[tmp], tmp});
				}
			}
		}
		for(auto i : t.second) ans[i.second] = dis[i.first % x] <= i.first;
	}
	for(int i = 1; i <= T; ++i) cout << (ans[i] ? "YES" : "NO") << "\n";
	return 0;
}
posted @ 2022-01-31 21:42  蒟蒻orz  阅读(13)  评论(0)    收藏  举报  来源