洛谷 P4240 毒瘤之神的考驗 解題報告
P4240 毒瘤之神的考驗
題目背景
\(\tt{Salamander}\)的家門口是一條長長的公路。
又是一年春天將至,\(\tt{Salamander}\)發現路邊長出了一排毒瘤!
\(\tt{Salamander}\)想帶一些毒瘤回家,但是,這時毒瘤當中鑽出來了一個毒瘤之神!
毒瘤之神:你想要帶毒瘤走嗎?想要帶走毒瘤,就必須回答我的問題!如果答不出來的話,你還是乖乖回家吧!
題目描述
毒瘤之神會問\(T\)次,每次給定\(n\),\(m\),\(\tt{Salamander}\)需要回答出\(\sum_{i=1}^n\sum_{j=1}^m\varphi(ij) \bmod 998244353\)
\(\tt{Salamander}\)這麼辣雞當然不會做啦,於是把問題丟給了你。
輸入輸出格式
輸入格式:
第一行包含一個正整數\(T\)。
接下來\(T\)行,每行包含兩個正整數,用空格隔開,表示這次詢問的\(n\),\(m\)。
輸出格式:
包含\(T\)行每行一個整數表示答案。
說明
對於\(40\%\)的資料,\(T=1,n,m\leq 10^5\);
對於\(50\%\)的資料,\(T\leq 1000,n,m\leq 10^5\);
對於另外\(10\%\)的資料,\(T\leq 10000,n=m\leq 10^5\);
對於\(100\%\)的資料,\(T\leq 10^4,n,m\leq 10^5\)
辣雞\(\tt{Dew}\)又雙叒叕做了20年這個題...
經驗:
\[\varphi(ij)=\frac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))}\]
1e5的多組資料,千萬別嫌吝嗇預處理的複雜度,\(\log\ln\)什麼的隨便往上扔
推式子一波得到
\[\sum_{T=1}^{\min(n,m)}\sum_{i=1}^{\lfloor\frac{n}{T}\rfloor}\varphi(iT)\sum_{j=1}^{\lfloor\frac{m}{T}\rfloor}\varphi(jT)\sum_{k|T}\frac{k}{\varphi(k)}\mu(\frac{T}{k})\]
發現萬惡之源了嗎,\(\varphi\)上面的\(T\)...
先不管那麼多,把式子簡化一下。
\[\mathbf G(i,j)=\sum_{k=1}^j\varphi(ki),\mathbf f(i)=\sum_{k|i}\frac{k}{\varphi(k)}\mu(\frac{i}{k})\]
注意\(\bf G\)的可用值只有\(n\ln n\)個,所以可用簡單的拿\(vector\)存一下,\(\bf f\)直接\(n\sqrt n\)做就好了。
式子變成了
\[\sum_{T=1}^{\min(n,m)}\mathbf G(T,\lfloor\frac{n}{T}\rfloor)\mathbf G(T,\lfloor\frac{m}{T}\rfloor)\mathbf f(T)\]
多組詢問的話,我們可以對詢問進行分塊。
預處理
\[\mathbf T(k,i,j)=\sum_{i=1}^k\mathbf G(k,i)\mathbf G(k,j)\mathbf f(k)\]
注意這個陣列同樣要使用\(vector\)存一下不可能使用的值,否則就爆了。
考慮後兩維預處理到\(U\),那麼預處理的複雜度可以簡單的認為是\(O(U^2n)\)的。
在詢問的時候,根據\(\lfloor\frac{n}{T}\rfloor\)或者\(\lfloor\frac{m}{T}\rfloor\)與\(U\)的大小關係判斷一下。
具體的,若\(\min(\lfloor\frac{n}{T}\rfloor,\lfloor\frac{m}{T}\rfloor)\le U\),直接用字首和\(\bf T\)做個差,複雜度最壞是\(O(\sqrt n)\)的
否則直接暴力,複雜度是\(O(\frac{n}{U})\)的
總複雜度是\(O(T(\sqrt n+\frac{n}{U})+n\ln n+n\sqrt n+nU^2)\)的
然後取個\(U=T^{\frac{1}{3}}\)大一點差不多最優了
Code:
#include <cstdio>
#include <vector>
#define ll long long
const ll N=1e5;
const ll U=30;
const ll mod=998244353;
ll pri[N+10],ispri[N+10],mu[N+10],phi[N+10],phiinv[N+10],f[N+10],cnt;
std::vector <ll> G[N+10],T[U+1][U+1];
ll quickpow(ll d,ll k)
{
ll f=1;
while(k)
{
if(k&1) f=f*d%mod;
d=d*d%mod;
k>>=1;
}
return f;
}
void init()
{
mu[1]=phi[1]=1;
for(ll i=2;i<=N;i++)
{
if(!ispri[i])
{
pri[++cnt]=i;
mu[i]=-1;
phi[i]=i-1;
}
for(ll j=1;j<=cnt&&i*pri[j]<=N;j++)
{
ispri[i*pri[j]]=1;
if(i%pri[j]==0)
{
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
else
{
mu[i*pri[j]]=-mu[i];
phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
}
}
for(ll i=1;i<=N;i++)
phiinv[i]=quickpow(phi[i],mod-2);
for(ll i=1;i<=N;i++)
{
ll j;
for(j=1;j*j<i;j++)
{
if(i%j==0)
{
(f[i]+=j*phiinv[j]%mod*mu[i/j])%=mod;
(f[i]+=i/j*phiinv[i/j]%mod*mu[j])%=mod;
}
}
if(j*j==i) (f[i]+=j*phiinv[j]%mod*mu[j])%=mod;
}
for(ll i=1;i<=N;i++)
{
G[i].push_back(0);
for(ll j=1;j*i<=N;j++)
G[i].push_back((G[i][j-1]+phi[i*j])%mod);
}
for(ll i=1;i<=U;i++)
{
for(ll j=1;j<=U;j++)
{
T[i][j].push_back(0);
for(ll k=1;k*i<=N;k++)
T[i][j].push_back((T[i][j][k-1]+f[k]*G[k][i]%mod*G[k][j])%mod);
}
}
}
ll min(ll x,ll y){return x<y?x:y;}
ll max(ll x,ll y){return x>y?x:y;}
int main()
{
init();
ll t,n,m;scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);ll ans=0;
for(ll l=1,r;l<=min(n,m);l=r+1)
{
r=min(n/(n/l),m/(m/l));
if(max(n/l,m/l)<=U) (ans+=T[n/l][m/l][r]-T[n/l][m/l][l-1])%=mod;
else
{
for(ll i=l;i<=r;i++)
(ans+=G[i][n/l]*G[i][m/l]%mod*f[i])%=mod;
}
}
printf("%lld\n",(ans+mod)%mod);
}
return 0;
}
2018.11.26