题目描述
难度分:1900
定义d(N)为N的因子个数。输入T(≤100)表示T组数据。所有数据的q之和≤1000。
每组数据输入n0(1≤n0≤106),q(1≤q≤1000)以及q询问。
初始化n=n0。然后输入q个询问,格式如下:
1 x
(1≤x≤106):把n更新为n×x,同时询问:是否存在一个正整数 a,满足gcd(n,a)=1且d(n×a)=n?输出YES
或NO
。2
:把n更新为n0。
保证任何时刻d(n)≤109。
输入样例1
7
1 5
1 1
1 2
2
1 8
1 9
20 4
1 3
2
1 7
1 12
16 10
1 6
1 6
1 10
1 9
1 1
1 9
1 7
1 3
1 2
1 10
9 1
1 3
8 1
1 2
8 3
1 5
1 8
1 10
11 5
1 8
1 2
1 1
1 3
1 1
输出样例1
YES
YES
YES
YES
YES
NO
YES
YES
NO
YES
YES
YES
NO
YES
NO
YES
YES
NO
NO
YES
NO
NO
YES
NO
NO
NO
NO
算法
质因数分解
这个题出思路还是挺快的,由于n≤106,x≤106,在极限情况下乘不了几次就会爆long long,因此需要对这些数都分解质因数。
对于输入的n0,先分解质因数存入到哈希表counter中,并保存一个副本哈希表cache。然后在线处理每一条询问:
- 对于第一类询问,将x也分解质因数,然后各个因子的个数都累加到counter中,这样counter就变成了n×x的质因数分解结果。首先注意到找到满足gcd(a,n)=1的a简直易如反掌,直接用counter中不存在的质因子组成a就可以了,这样一定可以保证n和a是互质的。再考虑d(n×a)=n,对于一个整数num,它可以进行唯一分解num=pα11pα12…pαkk,num的约数个数应该是(α1+1)(α2+1)…(αk+1)。而d(n)在本题中任何时候都不会超过109,说明不会爆int,先遍历counter计算出n的约数个数res,再对res进行质因数分解,将结果存到哈希表temp中。要想d(n×a)=n成立,只需要res|n成立即可,即temp是counter的子集。
- 对于第二类询问,将n又还原成n0,只需要将cache表再赋值给counter即可。
复杂度分析
时间复杂度
由于题面存在Σ的约束,这里只分析每个case的时间复杂度。分解质因数我用的是Pollard Rho算法,时间复杂度大概是4√n这个级别,先对n分解质因数,时间复杂度为O(4√n)。
然后处理每个询问,主要考虑第一类询问的时间复杂度,先对x分解质因数,时间复杂度为4√x,接下来合并n和x的质因子个数,在本题的数据范围下,质因子个数最多也就几百个,合并完成后计算n的约数个数,再判断res是否能被n整除,时间复杂度都是质因子个数的级别。
综上,整个算法的时间复杂度为O(4√n+q(4√x+A)),其中A为质因子个数的上限,x和n是一个级别的。
空间复杂度
空间消耗的瓶颈主要在于质因数分解的结果存储,因此额外空间复杂度可以近似看做O(A)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int t, n, q;
struct custom_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
// Miller-Rabin算法判断质数
LL fast_power(__int128 a, LL k, LL p) {
LL res = 1 % p;
while(k) {
if(k & 1) res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
bool miller_rabin(LL n) {
if(n == 2) return true;
if(n <= 1 || n % 2 == 0) return false;
LL base[7] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022};
LL u = n - 1, k = 0;
while(!(u&1)) u >>= 1, k++;
for(auto&x: base) {
if(x % n == 0) continue;
LL v = fast_power(x, u, n);
if(v == 1 || v == n - 1) continue;
for(int j = 1; j <= k; j++) {
LL last = v;
v = (__int128)v * v % n;
if(v == 1) {
if(last != n - 1) return false;
break;
}
}
if(v != 1) return false;
}
return true;
}
// Pollard-Rho分解质因数
LL Pollard_Rho(LL n) {
static mt19937_64 sj(chrono::steady_clock::now().time_since_epoch().count());
uniform_int_distribution<LL> u0(1, n - 1);
LL c = u0(sj);
auto f = [&](LL x) {
return ((__int128)x * x + c) % n;
};
LL x = f(0), y = f(x);
while(x != y) {
LL d = __gcd(abs(x - y), n);
if(d != 1) return d;
x = f(x), y = f(f(y));
}
return n;
}
void get_factor(LL n, unordered_map<LL, int, custom_hash>& counter) {
if(n == 1) return;
if(n == 4) {
counter[2] += 2;
return;
}
if(miller_rabin(n)) {
counter[n] += 1;
return;
}
LL x = n;
while(x == n) x = Pollard_Rho(n);
get_factor(x, counter);
get_factor(n/x, counter);
}
bool check(unordered_map<LL, int, custom_hash>& mp1, unordered_map<LL, int, custom_hash>& mp2) {
for(auto&[k, v]: mp2) {
if(!mp1.count(k) || mp1[k] < v) {
return false;
}
}
return true;
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &q);
unordered_map<LL, int, custom_hash> counter, cache;
get_factor(n, counter);
for(auto&[num, cnt]: counter) {
cache[num] = cnt;
}
for(int i = 1; i <= q; i++) {
int k, x;
scanf("%d", &k);
if(k == 1) {
scanf("%d", &x);
unordered_map<LL, int, custom_hash> temp1;
get_factor(x, temp1);
for(auto&[num, cnt]: temp1) {
counter[num] += cnt;
}
int res = 1; // n的因子个数
for(auto&[factor, cnt]: counter) {
res *= cnt + 1;
}
unordered_map<LL, int, custom_hash> temp2;
get_factor(res, temp2);
if(check(counter, temp2)) {
puts("YES");
}else {
puts("NO");
}
}else {
counter = cache;
}
}
puts("");
}
return 0;
}