P4240 毒瘤之神的考验

放弃复建……

观前提醒

本文含有巨量 公式,请确保渲染完成以获得最优的阅读体验。

题目简介

题目名称:毒瘤之神的考验
题目来源:洛谷月赛
评测链接:https://www.luogu.com.cn/problem/P4240

形式化题意:求:

取模。多测。

数据范围:

第一步,得把那个看起来非常恶心的式子给化开。

考虑 函数的定义式:

互质的数的个数,但我们还有一个更有用的式子:

其中 为质数。

所以,可以得到:

容易发现,对于 ,计算了 质因子分解的并集,而 的分开计算使得 的交集被重复计算了一次,所以我们需要除一遍 让两个式子达到平衡。

也就可以得到:

从而化简出:

至此,我们完成了本题的第一步。然后就是基本的反演推导(默认 ):

我们设 ,换枚举 为枚举 ,此时 变成了 的因数:

需要 的时间枚举,后半段 可以通过 时间预处理, 部分可以数论分块 时间预处理。

所以我们的时间复杂度为 ,至此,可以通过第一个


我们设:

化简原式:

前文已知, 可以在 时间内预处理出来,而根据观察,发现 存在递推公式:

所以 的预处理时间也是

所以现在的时间复杂度优化关键就在于前面的枚举了。我们将整个式子用函数表示:

可以发现一个递推式:

而对于中间的一段来说, 是相等的,所以可以考虑数论分块,写成差分形式:

那么,我们可以在 的时间内解决这个问题,至此,我们啥也通过不了。


容易发现,如果我们对 的所有 进行预处理的话,可以做到 的查询,但是预处理时空复杂度会上升到 ,所以我们考虑平衡规划阈值 ,我们只处理到 ,对后面的我们进行数论分块暴力查。

所以时间复杂度可以做到:,当 的时候取得最优。

AC Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// ----- Eternally question-----
// Problem: P4240 毒瘤之神的考验
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4240
// Memory Limit: 500 MB
// Time Limit: 2000 ms
// Written by: Eternity
// Time: 2023-06-30 08:39:28
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
x=0;
char ch=getchar(),t=0;
while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
template<>
inline void write(bool x){ putchar(x?'1':'0'); }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const int MAXN=1e5+10,MAXB=50;
const int Mod=998244353;
const int Uni=30;
bool Vis[MAXN];
int Mu[MAXN],Phi[MAXN],pri[MAXN],Tot;
bool is[MAXN];
int *G[MAXN],*S[MAXB][MAXB],F[MAXN];
int N,M,Test,inv[MAXN];
inline void sieve(int n)
{
Mu[1]=Phi[1]=inv[1]=1;
for(int i=2;i<=n;++i)
{
if(!is[i]) pri[++Tot]=i,Mu[i]=-1,Phi[i]=i-1;
for(int j=1;j<=Tot&&i*pri[j]<=n;++j)
{
is[i*pri[j]]=1;
if(i%pri[j]==0)
{
Mu[i*pri[j]]=0,Phi[i*pri[j]]=Phi[i]*pri[j];
break;
}
Mu[i*pri[j]]=-Mu[i],Phi[i*pri[j]]=Phi[i]*(pri[j]-1);
}
}
}
inline void init(int n)
{
for(int i=2;i<=n;++i) inv[i]=Mod-(ll)Mod/i*inv[Mod%i]%Mod;
for(int i=1;i<=n;++i) for(int j=1;i*j<=n;++j) F[i*j]=(F[i*j]+(ll)i*inv[Phi[i]]%Mod*Mu[j]%Mod+Mod)%Mod;
for(int i=1;i<=n;++i)
{
G[i]=new int [n/i+1];
G[i][0]=0;
for(int j=1;j<=n/i;++j) G[i][j]=(G[i][j-1]+(ll)Phi[i*j])%Mod;
}
for(int j=1;j<=Uni;++j)
for(int k=1;k<=Uni;++k)
{
int len=(n/std::max(j,k));
S[j][k]=new int [len+1];
S[j][k][0]=0;
for(int i=1;i<=len;++i) S[j][k][i]=(S[j][k][i-1]+(ll)F[i]*G[i][j]%Mod*G[i][k]%Mod)%Mod;
}
}
inline ll solve(int n,int m)
{
if(n>m) std::swap(n,m);
ll ans=0;
for(int i=1;i<=m/Uni;++i) ans=(ans+(ll)F[i]*G[i][n/i]%Mod*G[i][m/i]%Mod)%Mod;
for(int l=m/Uni+1,r;l<=n;l=r+1)
{
r=std::min(n/(n/l),m/(m/l));
ans=(ans+(ll)(S[n/l][m/l][r]-S[n/l][m/l][l-1]+Mod)%Mod+Mod)%Mod;
}
return ans;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(Test);
sieve(MAXN-10),init(MAXN-10);
while(Test--)
{
read(N,M);
write(solve(N,M),'\n');
}
return 0;
}
/*

*/