题解 P4449 【于神之怒加强版】
Update on 2021.2.4:修复了一些小错误
Update on 2021.9.1:再学线筛积性函数,补充解释,看的更清楚 (可能吧)。
Update on 2021.9.2:新增关于
题外话:我研究了一下午题解终于搞懂了这道题,为了避免他人像我一样重(zhou)蹈(ma)覆(ti)辙(jie),准备把我所理解的所有有关这道题的全部写出来。
首先,这个题目要求我们求这样一个式子:
一看到
于是,我们开始按照莫反的思维推式子。
设
其中
然后就可以得到
根据反演,就可以知道
那么答案:
其中
然后可以改一下和号的顺序:
这一切看起来都是那么的和谐,前面可以用整除分块搞掉。
这题,要切掉了吧?
“好水啊”你心里暗自感叹着。
可是,当你注意后面那一坨以后,你可能会直接懵掉。
你尝试把他变成
后面这一坨还是搞不出来,不然
可是你又不甘心。
我们发现后面的那一坨是一个卷积的形式,并且两个函数均为积性函数,所以卷起来也是积性函数。考虑线性筛。
设:
那么答案就成了:
可是,
(update的另一种求法在第一份代码后面)
就是这个问题,困扰我许久,翻遍了所有题解,才总结出这个自认为易懂一点的解释:
先扔结论:
首先第一个式子应该很好证明吧:
根据定义易得,
关键就是第二个式子怎么证,证了就可以在线性筛的时候处理了,但它的证明也困扰了我一个多小时。
首先设:
注意到
根据
然后设
这里解释一下,因为只有当
再看一下该怎么化简。尝试把
这里是对于任意一个质数的
然后我们发现,只有当
然后就可以带回原式了:
第二个式子也就证明出来了。
这里总结一下做法,先用线性筛把
然后对于每一组询问,跑一次整除分块,单次复杂度
总复杂度
请别介意我巨丑的代码:
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=5e6+5,mod=1e9+7;
int g[N],pri[N],sum[N];
bool v[N];
inline int read()
{
char h=getchar();
int y=0,q=1;
while(h<'0'||h>'9'){if(h=='-')q=-1;h=getchar();}
while(h>='0'&&h<='9'){y=y*10+h-'0';h=getchar();}
return y*q;
}
inline int qpow(int a,int b)
{
int j=1;
while(b)
{
if(b&1)(j*=a)%=mod;
(a*=a)%=mod;
b>>=1;
}
return j;
}
inline void init(int k)
{
g[1]=1;
int tot=0;
for(int i=2;i<N;i++)
{
if(!v[i]){pri[++tot]=i;g[i]=(qpow(i,k)-1+mod)%mod;}
for(int j=1;j<=tot&&pri[j]*i<N;j++)
{
v[pri[j]*i]=1;
if(i%pri[j]==0)
{
g[pri[j]*i]=(g[i]*((g[pri[j]]+1)%mod))%mod;//g[pri[j]]+1=pri[j]^k
break;
}
g[pri[j]*i]=(g[i]*g[pri[j]])%mod;
}
}
for(int i=1;i<N;i++)sum[i]=(sum[i-1]+g[i])%mod;
}
inline int f(int a,int b)
{
int n=min(a,b),ans=0;
for(int l=1,r;l<=n;l=r+1)
{
r=min(a/(a/l),b/(b/l));
ans=(ans+(sum[r]-sum[l-1])*(a/l)%mod*(b/l)%mod+mod)%mod;
}
return ans;
}
signed main()
{
int t=read(),k=read();
init(k);
while(t--)
{
int n=read(),m=read();
printf("%lld\n",f(n,m));
}
}
update:
我们发现,上面的求解证明有点复杂。作为一名专业的懒癌oier,肯定要用某个特定的规律来做这种题。
首先,对于质数,我们一般很好求它的值,就直接算了。
然后对于筛那部分,假如
然后我们就考虑不互质的情况了。我们尝试把
但是我们发现,对于
复杂度仍然为
上一下筛的代码就行了:
inline void init(int k)
{
g[1]=1;
int tot=0;
for(int i=2;i<N;i++)
{
if(!v[i]){pri[++tot]=i;g[i]=(qpow(i,k)-1+mod)%mod;t[i]=i;ti[i]=1;}
for(int j=1;j<=tot&&pri[j]*i<N;j++)
{
v[pri[j]*i]=1;
if(i%pri[j]==0)
{
t[i*pri[j]]=t[i]*pri[j];
ti[i*pri[j]]=ti[i]+1;
if(t[i]==i)
{
g[pri[j]*i]=qpow(pri[j],(ti[i]+1)*k)-qpow(pri[j],ti[i]*k);
if(g[pri[j]*i]<0)g[pri[j]*i]+=mod;
}
else
{
g[i*pri[j]]=1ll*g[i/t[i]]*g[t[i]*pri[j]]%mod;
}
break;
}
g[pri[j]*i]=(g[i]*g[pri[j]])%mod;
t[i*pri[j]]=pri[j];ti[i*pri[j]]=1;
}
}
for(int i=1;i<N;i++)sum[i]=(sum[i-1]+g[i])%mod;
}