题解 P4240【毒瘤之神的考验】
简化题面
求
答案对\(998244353\)取模。多组询问
\(1\le T\le 10^4\),\(1\le n,m\le 10^5\)
解题思路
这题在我计划里面躺了最少有五个月了,当时觉得好难啊就没做= =,最近拿出来看看好简单啊于是就做了
考虑大力推柿子
对于\(\varphi{(i\times j)}\),我们有一个不是很显然的也不是很常见的结论,
考虑如何证明,我们将\(\varphi\)都用定义拆开,可以写成比较鬼畜的形式
显然两边是相等的。
那么既然已经知道了这玩意那我们直接就可以做了
显然到这里没办法继续推了,考虑如何进行预处理。
第一部分显然可以在\(O(nlogn)\)的时间内进行预处理得到
那么考虑如何处理后面的玩意,我们令\(g(k,n)=\sum_{i=1}^n \varphi{(i\times k)}\),这个玩意显然是可以递推的\(g(i,j)=g(i,j-1)+\varphi{(i\times j)}\)
于是这个玩意又可以在\(O(nlogn)\)的时间复杂度内预处理出来
那么原柿子现在变成了
那么到这里一个很自然的想法是我们可以进行分块打表预处理
考虑设\(t(i,j,Q)\),前面的\(i,j\)是分别处于的块\(\frac{n}{Q}\)和\(\frac{m}{Q}\),这个玩意还是可以递推的\(t(i,j,Q)=t(i,j,Q-1)+\sum_{p|Q} \frac{p\mu{(\frac{Q}{p})}}{\varphi{(p)}} g(Q,i) g(Q,j)\)
我们直接设定大小为\(len\)的块暴力\(O(n^2)\)预处理就可以了,最后统计答案的时候散块暴力整块直接分块统计答案就可以了。
时间复杂度是\(O(nlogn+nlen^2+T(\sqrt n+\frac{n}{len}))\)
\(\mathcal{Code}\)
// Author: Ame__
#include<bits/stdc++.h>
#include<stdint.h>
#define _ 0
#define AME__DEBUG
#define bomb exit(0)
#define LOG(FMT...) fprintf(stderr , FMT)
#define TOWA(FMT...) fprintf(stdout , FMT)
using namespace std;
/*Grievous Lady*/
typedef int32_t i32;
typedef int64_t i64;
typedef double qwq;
const int BUF_SIZE = 1 << 12;
char buf[BUF_SIZE] , *buf_s = buf , *buf_t = buf + 1;
#define PTR_NEXT() \
{ \
buf_s ++; \
if(buf_s == buf_t) \
{ \
buf_s = buf; \
buf_t = buf + fread(buf , 1 , BUF_SIZE , stdin); \
} \
}
#define mians(_s_) \
{ \
while(!isgraph(*buf_s)) PTR_NEXT();\
char register *_ptr_ = (_s_); \
while(isgraph(*buf_s) || *buf_s == '-') \
{ \
*(_ptr_ ++) = *buf_s; \
PTR_NEXT(); \
} \
(*_ptr_) = '\0'; \
}
template <typename _n_> void mian(_n_ & _x_){
while(*buf_s != '-' && !isdigit(*buf_s)) PTR_NEXT();
bool register _nega_ = false; if(*buf_s == '-'){ _nega_ = true; PTR_NEXT(); }
_x_ = 0; while(isdigit(*buf_s)){ _x_ = _x_ * 10 + *buf_s - '0'; PTR_NEXT(); } if(_nega_) _x_ = -_x_;
}
const i32 kato = 1e5 + 10;
const i32 atri = 1e5;
const i32 deco = 1e2 + 10;
const i32 mod = 998244353;
template <typename _n_> bool cmax(_n_ &a , const _n_ &b){ return a < b ? a = b , 1 : 0; }
template <typename _n_> bool cmin(_n_ &a , const _n_ &b){ return a > b ? a = b , 1 : 0; }
i32 prime[kato] , phi[kato] , mu[kato] , inv[kato] , sum[kato];
bool ispri[kato];
i32 n , m , cnt , T , b = 50;
i32 *g[kato] , *t[deco][deco];
inline i32 quick_pow(i32 a , i32 b){
i32 res = 1;
for(; b ; b >>= 1 , a = static_cast<i64>(a) * a % mod){
if(b & 1){
res = static_cast<i64>(res) * a % mod;
}
}
return res;
}
inline void pri(){
for(i32 i = 2;i <= atri;i ++){
if(!ispri[i]){
prime[++ cnt] = i;
phi[i] = i - 1;
mu[i] = -1;
}
for(i32 j = 1;j <= cnt && i * prime[j] <= atri;j ++){
ispri[i * prime[j]] = 1;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
mu[i * prime[j]] = -mu[i];
}
inv[i] = quick_pow(phi[i] , mod - 2);
}
for(i32 i = 1;i <= atri;i ++){
for(i32 j = i , d = 1;j <= atri;j += i , d ++) sum[j] = ((sum[j] + static_cast<i64>(i) * inv[i] % mod * mu[d] % mod) + mod) % mod;
}
for(i32 i = 1;i <= atri;i ++){
g[i] = new i32[atri / i + 10] , g[i][0] = 0;
for(i32 j = 1;j <= atri / i;j ++) g[i][j] = (g[i][j - 1] + phi[i * j]) % mod;
}
for(i32 i = 1;i <= b;i ++){
for(i32 j = i;j <= b;j ++){
i32 len = atri / max(i , j);
t[i][j] = new i32[len + 10] , t[i][j][0] = 0;
for(i32 k = 1;k <= len;k ++) t[i][j][k] = (t[i][j][k - 1] + static_cast<i64>(sum[k]) * g[k][i] % mod * g[k][j] % mod) % mod;
}
}
}
inline int Ame_(){
#ifdef AME__
freopen(".in" , "r" , stdin); freopen(".out" , "w" , stdout); int nol_cl = clock();
#endif
mian(T); phi[1] = mu[1] = inv[1] = 1; pri();
for(; T --> 0 ;){
mian(n) , mian(m);
if(n > m) swap(n , m);
i32 ans = 0;
for(i32 i = 1;i <= m / b;i ++) ans = (ans + static_cast<i64>(sum[i]) * g[i][n / i] % mod * g[i][m / i] % mod) % mod;
for(i32 l = m / b + 1 , r;l <= n;l = r + 1){
r = min(n / (n / l) , m / (m / l));
ans = ((ans + t[n / l][m / l][r] - t[n / l][m / l][l - 1]) % mod + mod) % mod;
}
TOWA("%d\n" , ans);
}
#ifdef AME__TIME
LOG("Time: %dms\n", int((clock() - nol_cl) / (qwq)CLOCKS_PER_SEC * 1000));
#endif
return ~~(0^_^0); /*さようならプログラム*/
}
int Ame__ = Ame_();
int main(){;}