怜香惜玉
分析
观察(直到考试最后一小时我才尝试打表)发现若(gcd(x,i)=1),则(gcd(x,x-i)=1),所以是对称的。
那么又由于乘了2,所以(f(x)=x)。
然后打个快速幂,求个前缀和就行了。
时间复杂度(O(n log n))
co int MAXN=1e6+1,mod=998244353;
int qpow(int x,int k)
{
int res=1;
while(k)
{
if(k&1)
res=(ll)res*x%mod;
x=(ll)x*x%mod,k>>=1;
}
return res;
}
int ans[MAXN];
int main()
{
int T,k;
read(T);read(k);
for(int i=1;i<MAXN;++i)
{
ans[i]=qpow(i,k);
(ans[i]+=ans[i-1])%=mod;
}
while(T--)
{
printf("%d
",ans[read<int>()]);
}
return 0;
}
拓展
L巨:这题是套路,经常用到。
然而由于L巨太巨了,所以L巨还是帮我把这个式子用莫比乌斯反演证明出来了。
要证明的是
[sum_{i=0}^{n}i cdot [gcd(i,n)=1] = frac{(varphi(n) + e(n)) cdot n}{2}
]
加上一个e是因为按题意phi(1)=2
证明
[左边 \
=sum_{i=0}^{n} i cdot sum_{d|i且d|n} mu(d) \
=sum_{d|n} mu(d) sum_{i=0}^{lfloor frac{n}{d}
floor} d cdot i \
=sum_{d|n} mu(d) cdot d cdot frac{frac{n}{d}(frac{n}{d}+1)}{2} \
=frac{n}{2}(mu*id+e)(n) \
=frac{n cdot (varphi(n) + e(n))}{2} \
=右边
]
解释
第三步d整除n,所以去掉了下取整符号,转化为等差数列求和。
倒数第二步在消掉分子分母中同时出现的数。
倒数第一步,由于有
[mu * 1 = e \
varphi * 1 = id
]
所以交叉卷积,消去1得到
[mu * id = varphi
]
LCMSum
给定(n(n leq 10^6)),求
[sum_{i=1}^{n} extrm{lcm}(i,n)
]
有(T(T leq 3 imes 10^5))组询问。
分析
https://blog.sengxian.com/solutions/bzoj-2226?tdsourcetag=s_pcqq_aiomsg
[sum_{i=1}^{n} extrm{lcm}(i,n) \
=sum_{i=1}^{n}frac{i cdot n}{gcd(i,n)}\
=n sum_{d|n}frac{1}{d}sum_{i=1}^{n}[gcd(i,n)=d] cdot i\
=n sum_{d|n}frac{1}{d}sum_{i=1}^{frac{n}{d}}[gcd(i,frac{n}{d})=1] cdot i cdot d \
= n sum_{d|n} sum_{i=1}^{frac{n}{d}} [gcd(i,frac{n}{d})=1] cdot i
]
后半部分参照上一题,可得
[原式
=n sum_{d|n} frac{(varphi(n) + e(n)) cdot n}{2} \
=frac{n}{2} sum_{d|n} (varphi(n) + e(n)) cdot n
]
现在的这个后面的式子(O(n))线性筛后,枚举倍数(O(n ln n))预处理。
然后就能(O(1))回答每次询问。
时间复杂度(O(n + n ln n + T))
co int MAXN=1e6+7;
int prime[MAXN],pcnt;
int phi[MAXN];
ll f[MAXN];
void linear()
{
prime[1]=1;
phi[1]=1;
for(int i=2;i<MAXN;++i)
{
if(!prime[i])
{
prime[++pcnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=pcnt&&i*prime[j]<MAXN;++j)
{
prime[i*prime[j]]=1;
phi[i*prime[j]]=phi[i]*phi[prime[j]];
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
for(int i=1;i<MAXN;++i)
for(int j=i;j<MAXN;j+=i)
f[j]+=(ll)phi[i]*i;
}
ll solve(int n)
{
return (f[n]+1)*n/2;
}
int main()
{
linear();
int T=read<int>();
while(T--)
{
printf("%lld
",solve(read<int>()));
}
return 0;
}