P7451 [THUSCH2017] 杜老师
P7451 [THUSCH2017] 杜老师
思路
注意到完全平方数在质因数分解之后所有质数的指数都是偶数,既然只关心奇偶性,我们在 \(\bmod 2\) 意义下考虑这个问题,而如果将两个数乘在一起,它们的指数相加,在 \(\bmod 2\) 意义下等价于异或指数,指数看作二进制数,如果我们得到了 \(l\sim r\) 的异或线性基,这就是一个线性基的经典问题了。
如果对区间每个数暴力分解质因数之后插入线性基,这样的复杂度是 \(O((r - l)(\sqrt V + \pi^2(V)))\),如果套一个 bitset
可以获得 \(\dfrac 1\omega\) 的优秀常数。
熟知一个数 \(x\) 最多只有一个因数大于 \(\sqrt x\),直接开值域个线性基相当浪费,所以把这些大因数单独开一个 unordered_map
存起来就好了,这样的复杂度是 \(O(\dfrac{1}{\omega}(r - l)(\sqrt V + \pi^2(\sqrt V)))\) 的。
需要优化,通过观察题解,发现了这个结论:
如果区间长度大于 \(2\sqrt V\) 左右,每一个在区间内出现的质因子都会被插入线性基。
有了这个,对于长度大于 \(2\sqrt V\) 的就查质因子,对于长度不大于 \(2\sqrt V\) 的就用上一个方法做。
代码比较好写。
// Problem: P7451 [THUSCH2017] 杜老师
// Author: Moyou
// Copyright (c) 2024 Moyou All rights reserved.
// Date: 2024-01-29 18:40:17
#include <iostream>
#include <bitset>
#include <unordered_map>
using namespace std;
const int N = 1e7 + 10, mod = 998244353, B = 6500, M = 460; // magic number
int l, r, primes[N], tot;
bool st[N];
void sieve() {
for(int i = 2; i < N; i ++) {
if(!st[i]) primes[++ tot] = i;
for(int j = 1; j <= tot && primes[j] * i < N; j ++) {
st[i * primes[j]] = 1;
if(i % primes[j] == 0) break;
}
}
}
bitset<M> ba[M], tmp;
unordered_map<int, bitset<M> > big;
int bsiz;
int qmi(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = 1ll * res * a % mod;
b >>= 1, a = 1ll * a * a % mod;
}
return res;
}
void work() {
cin >> l >> r;
if(r - l + 1 >= B) {
int cnt = 0;
for(int i = 1; i <= tot && primes[i] <= r; i ++) {
if(r / primes[i] != (l - 1) / primes[i])
cnt ++;
}
cout << qmi(2, r - l + 1 - cnt) << '\n';
}
else {
bsiz = 0;
big.clear();
for(int i = 0; i < M; i ++) ba[i].reset();
for(int i = l, t; i <= r; i ++) {
t = i, tmp.reset();
for(int j = 1; j <= tot && t > 1 && primes[j] * primes[j] < N; j ++)
while(t % primes[j] == 0) t /= primes[j], tmp[j - 1] = 1 ^ tmp[j - 1];
if(t > 1) {
if(!big.count(t)) {
bsiz ++, big[t] = tmp;
goto ne; // goto 谁用谁爽
}
tmp ^= big[t];
}
for(int i = M - 1; ~i; i --)
if(tmp[i]) {
if(!ba[i].any()) {
bsiz ++, ba[i] = tmp;
break;
}
tmp ^= ba[i];
}
ne:;
}
cout << qmi(2, r - l + 1 - bsiz) << '\n';
}
return ;
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
sieve();
while (T--) work();
return 0;
}