7.P2231 [HNOI2002]跳蚤
题目大意
求 \(\sum a_i\cdot x_i=1\) \((1\le N\le M\le 10^8,M^N\le10^{16})\)解的个数
分析
一道比较有意思的题
根据裴蜀定理, \(\sum a_i\cdot x_i=d\) 有解,当且仅当 \(\gcd({a_i})|d\)
在本题中\(d=1\),即
\[\begin{align*} s &=\sum_{i\in[1,n],a_i\in[1,m]}[gcd(a_i,m)=1] \\&=\sum_{i\in[1,n],a_i\in[1,m]}\sum_{d|gcd(a_i,m)}\mu(d) \\&=\sum_{d|m}\mu(d)\sum_{i\in[1,n],a_i\in[1,m]}[d|a_i] \\&=\sum_{d|m}\mu(d)(\frac{m}{d})^n \end{align*} \]可以暴力\(O(\sqrt{m})\)枚举\(d|m\),\(O(\sqrt{d})\)计算\(\mu(d)\)
复杂度理论上限为\(O(N)\),不过因为存在不少\(\mu(d)=0\),跑不满\(O(\sqrt{d})\),所以可过(甚至挺快的)
但是,这样不够优美
考虑将\(m\)质因数分解
\[m=\prod {p_i}^{k_i} \]因为当\(d\)有大于\(1\)的平方因数是\(\mu(d)=0\),对答案无贡献
又因为\(d|m\),所以\(d\)的质因子必为\(m\)的质因子
所以可以枚举质因子
\[s=\mu(1)\cdot m^n+\sum \mu(p_i)\cdot(\frac{m}{p_i})^n+\sum\mu(p_i\cdot p_j)\cdot(\frac{m}{p_i\cdot p_j})^n+…… \]根据\(\mu(d)\)的定义
\[\begin{align*} s&=m^n-\sum(\frac{m}{p_i})^n+\sum(\frac{m}{p_i\cdot p_j})^n-…… \\&=m^n\cdot(1-\sum(\frac{1}{p_i})^n+\sum(\frac{1}{p_i\cdot p_j})^n-……) \\&=m^n\cdot\prod(1-\frac{1}{p_i^n}) \end{align*} \]就可以\(O(\sqrt{m}\log n)\)求解(为啥跟暴力时间一样?)
暴力O(n)代码
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(long long x){
if(x<0) x=-x,putchar('-');
ri short tl=0,a[20]={0};
do{a[++tl]=x%10,x/=10;}while(x);
for(;tl;--tl)putchar(a[tl]+48);
}
il int mu(int n){
int res=n,as=1;
for(ri int i=2;i*i<=n;++i){
if(res%i==0){
res/=i;
if(res%i==0) return 0;
as=-as;
}
}
if(res>1) as=-as;
return as;
}
il long long qpow(int a,int b){
long long as=1,aa=a;
while(b){
if(b&1) as*=aa;
aa*=aa,b>>=1;
}
return as;
}
int main(){
int n=rd(),m=rd();
long long as=0;
for(ri int i=1,v;i*i<=m;++i){
if(m%i==0){
v=m/i,as+=1ll*mu(i)*qpow(v,n);
if(v!=i) as+=1ll*mu(v)*qpow(i,n);
}
}
wt(as);
return 0;
}
优化后代码
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(long long x){
if(x<0) x=-x,putchar('-');
ri short tl=0,a[20]={0};
do a[++tl]=x%10,x/=10; while(x);
while(tl) putchar(a[tl--]+48);
}
il long long qpow(int a,int b){
ri long long as=1,aa=a;
while(b){
if(b&1) as*=aa;
aa*=aa,b>>=1;
}
return as;
}
int main(){
int n=rd(),m=rd();
ri int res=m,ls=m;
ri long long as=1;
for(ri int i=2;i*i<=res;++i){
if(res%i==0){
ls/=i,as*=(qpow(i,n)-1);
while(res%i==0) res/=i;
}
}
if(res>1) as*=(qpow(res,n)-1),ls/=res;
as*=qpow(ls,n);
wt(as);
return 0;
}
8.P1390 公约数的和
题目大意
求\(\sum_{i=1}^n\sum_{j=i+1}^n\gcd(i,j)\) \((2\le n\le2\times10^6)\)
分析
发现这个式子不是很好求
可以考虑先求
\[s=\sum_{i=1}^n\sum_{j=1}^n\gcd(i,j) \]这个式子\(T3\)求过,就不在赘述(绝对不是我懒得写)
但是多算了一部分,考虑如何去掉
可以发现
\[\sum_{i=1}^n\sum_{j=i+1}^n\gcd(i,j)=\sum_{i=2}^n\sum_{j=1}^{i-1}\gcd(i,j) \]所以除去\(i=j\)的情况外,题目要求计算的式子其实被算了两次
那\(i=j\)的情况呢?
我们知道\(\gcd(a,a)=a\),所以
\[\sum_{i\in[1,n],j=i}\gcd(i,j)=\sum_{i=1}^n i=\frac{n\times(1+n)}{2} \]那么
\[as=\frac{s-\frac{n\times(1+n)}{2}}{2} \]还是可以\(O(n+n\cdot\sqrt{n})\) (这不是四倍经验吗)
AC·code(莫反)
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long
using namespace std;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
ri short tl=0,a[15]={0};
do{a[++tl]=x%10,x/=10;}while(x);
for(;tl;--tl)putchar(a[tl]+48);
}
cs int N=2e6;
int ss[N+5],cnt,vs[N+5],mu[N+5],sum[N+5];
il void init(int n){
mu[1]=sum[1]=1;
for(ri int i=2;i<=n;++i){
if(!vs[i]) ss[++cnt]=i,mu[i]=-1;
for(ri int j=1;j<=cnt&&ss[j]*i<=n;++j){
vs[i*ss[j]]=1;
if(i%ss[j]==0) break;
mu[i*ss[j]]=-mu[i];
}
sum[i]=sum[i-1]+mu[i];
}
}
signed main(){
int n=rd(),m,as=0;
init(N);
for(ri int d=1;d<=n;++d){
m=n/d;
for(ri int l=1,r=0;l<=m;l=r+1){
r=m/(m/l);
as+=d*(sum[r]-sum[l-1])*(m/l)*(m/l);
}
}
wt((as-(n*(n+1)/2))/2);
return 0;
}
不过\(T3\)可以用简(玄)洁(学)做法做,那么同理这题也可以
递推容斥(短小)
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define F(s) freopen(#s".in","r",stdin),freopen(#s".out","w",stdout);
using namespace std;
cs int N=2e6+5;
signed main(){
// F(1)
int n,f[N]={0},ans=0;
cin>>n;
for(ri int i=n;i;--i){
f[i]=(n/i)*(n/i);
for(ri int j=2;j*i<=n;++j){
f[i]-=f[i*j];
}
ans+=i*f[i];
}
cout<<(ans-n*(n+1)/2)/2;
//只T3多了这一步操作
//我绝对不会告诉你这就是从T3粘的
return 0;
}
最优解(优化欧拉反演?)
//sto最优解orz (看不懂)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e4 + 10;
const int MAXM = 1.5e3 + 10;
int p[MAXN], tot;
int phi[MAXN];
bitset<MAXN> vis;
inline
void init(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) p[++tot] = i, phi[i] = i - 1;
for (int j = 1; j <= tot; j++) {
if (i * p[j] > n) break;
vis[i * p[j]] = 1;
if (i % p[j] == 0) { phi[i * p[j]] = phi[i] * p[j]; break; }
phi[i * p[j]] = phi[i] * (p[j] - 1);
}
}
for (int i = 1; i <= n; i++) phi[i] += phi[i - 1];
}
inline
ll sum(ll n) {
return n * (n + 1) >> 1;
}
ll t1[MAXM], t2[MAXM];
int lim, n, k;
inline
ll add(int p, ll x) {
return p <= k ? t1[p] = x : t2[n / p] = x;
}
inline
ll get(int p) {
return p <= k ? t1[p] : t2[n / p];
}
ll sump(ll x) {
if (x <= lim) return phi[x];
if (get(x)) return get(x);
ll res = sum(x);
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
res -= (r - l + 1) * sump(x / l);
}
return add(x, res);
}
inline
ll solve(ll x) {
ll res = 0;
for (ll l = 1, r; l <= x; l = r + 1) {
r = x / (x / l);
res += (sump(x / l) - 1) * (sum(r) - sum(l - 1));
}
return res;
}
int main() {
scanf("%d", &n);
k = sqrt(n), lim = cbrt(n);
lim *= lim;//pow(n,2.0/3.0)
init(lim);
printf("%lld", solve(n));
}
9.P6810 「MCOI-02」Convex Hull 凸包
题目大意
求\(\sum_{i=1}^n\sum_{j=1}^m\tau(i)\cdot\tau(j)\cdot\tau(\gcd(i,j))\)
分析
莫反做法
因为写的是莫反,所以先上莫反做法(前置知识狄利克雷卷积)
- 证明\(\tau*\mu=I\)
上文提到过
\[\sum_{d|n}\mu(d)\cdot1=[n=1] \]写成卷积形式有
\[I*\mu=\epsilon \]展开\(\tau\)可得
\[\tau(n)=\sum_{d|n}1\cdot1 \]写成卷积形式有
\[\tau=I*I \]将上式等式两边同时卷\(\mu\),可得
\[\tau*\mu=I*I*\mu \]用\(\epsilon\)换\(I*\mu\),可得
\[\tau*\mu=I*\epsilon \]因为
\[I*\epsilon=I \]所以证得
\[\tau*\mu=I \]- 开始推式子
令\(T=d\cdot g\)
\[s=\sum_{T=1}^n\sum_{d|T}\tau(d)\cdot\mu(\frac{T}{d})\cdot\sum_{i=1}^{\lfloor\frac{n}{T}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{T}\rfloor}\tau(i\cdot T)\cdot\tau(j\cdot T) \]由于\(\tau*\mu=I\),即
\[\sum_{d|T}\tau(d)\cdot\mu(\frac{T}{d})=1 \]于是
\[\begin{align*} s&=\sum_{T=1}^{n}\sum_{i=1}^{\lfloor\frac{n}{T}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{T}\rfloor}\tau(i\cdot T)\cdot\tau(j\cdot T) \\&=\sum_{T=1}^{n}\sum_{i=1}^{\lfloor\frac{n}{T}\rfloor}\tau(i\cdot T)\cdot\sum_{j=1}^{\lfloor\frac{m}{T}\rfloor}\tau(j\cdot T) \\&=\sum_{T=1}^n\sum_{T|i}^n\tau(i)\sum_{T|j}^m\tau(j) \end{align*} \]这样就可以\(O(n\cdot\log n)\)计算(可过)
- 狄利克雷后缀和
题解说可以用Dirichlet 后缀和优化,模板Dirichlet 前缀和(把前缀和倒着写就好了)
对于形如\(\sum_{T|i}f(i)\)的式子,可以优化到\(O(n\cdot\log\log n)\)
对于每一个质因数,考虑其对后缀和的贡献即可(不是很懂)
狄利克雷后缀和
int ss[N],cnt,sum[N];
//质数,质数个数,后缀和
il void init(int n){
for(ri int i=1;i<=cnt;++i){
for(ri int j=n/ss[i];j;--j){
sum[j]=(sum[j]+sum[j*ss[i]])%mod;
}
}
return;
}
玄学做法
这是第一篇题解(验题人)的做法,非常简洁……
考虑\(\tau\)的定义
\[\begin{align*} s&=\sum_{i=1}^n\sum_{j=1}^m\tau(i)\cdot\tau(j)\cdot\sum_{T|\gcd(i,j)}1 \\&=\sum_{T=1}^n\sum_{T|i}^n\tau(i)\cdot\sum_{T|j}^m\tau(j) \end{align*} \]这不就是上面用莫反推出来的式子吗
暴力计算O(n log n)代码
#include<bits/stdc++.h>
#define cs const
#define ri register
#define il inline
using namespace std;
int n,m,p;
long long as;
cs int N=2e6+5;
int t[N];
il void init(int n){
for(ri int i=1;i<=n;++i)
for(ri int j=1;j*i<=n;++j) t[i*j]++;
}
il int sn_mul_sm(int k){
ri long long sn=0,sm=0;ri int i;
for(i=1;i*k<=n;++i) (sn+=t[i*k])%=p;sm=sn;
for(;i*k<=m;++i) (sm+=t[i*k])%=p;
return sn*sm%p;
}
int main(){
cin>>n>>m>>p;if(n>m) swap(n,m);init(m);
for(ri int i=1;i<=n;++i) (as+=sn_mul_sm(i))%=p;
return cout<<as,0;
}
狄利克雷后缀和优化O(n log log n)代码
//题解上粘的比较优美的代码
#include <cstdio>
#include <cmath>
#include <iostream>
#include <ctime>
#include <algorithm>
#include <map>
using namespace std;
int n, m, p, ans, len, T[2000005], prime[2000005], a[2000005], b[2000005], e[2000005];
bool vis[2000005];
void seive() {
T[1] = 1;
for (int i = 2; i <= m; i++) {
if (!vis[i])
prime[++len] = i, T[i] = 2, e[i] = 1;
for (int j = 1; j <= len && i * prime[j] <= m; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
T[i * prime[j]] = T[i] / (e[i] + 1) * (e[i] + 2);
e[i * prime[j]] = e[i] + 1;
break;
} else
T[i * prime[j]] = T[i] * 2, e[i * prime[j]] = 1;
}
}
}
int main() {
scanf("%d %d %d", &n, &m, &p);
if (n > m)
swap(n, m);
seive();
for (int i = 1; i <= m; i++) {
if (i <= n)
a[i] = T[i];
b[i] = T[i];
}
for (int i = 1; i <= len; i++)
for (int j = n / prime[i]; j; j--) a[j] = (a[j] + a[j * prime[i]]) % p;
for (int i = 1; i <= len; i++)
for (int j = m / prime[i]; j; j--) b[j] = (b[j] + b[j * prime[i]]) % p;
for (int i = 1; i <= n; i++) ans = (ans + 1ll * a[i] * b[i]) % p;
printf("%d\n", ans);
return 0;
}
最优解(应该是O(n log log n))
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 5;
int f[N];
int g[N];
int sumn[N];
int summ[N];
bool vis[N];
vector<int> p;
int n, m, k;
void Euler(int n)
{
f[1] = 1;
g[1] = 1;
vis[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!vis[i])
{
p.emplace_back(i);
g[i] = 1;
f[i] = 2;
}
for (int j = 0; j < p.size() && i * p[j] <= n; j++)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{
g[i * p[j]] = g[i] + 1;
f[i * p[j]] = f[i] / (g[i] + 1) * (g[i * p[j]] + 1);
break;
}
g[i * p[j]] = 1;
f[i * p[j]] = f[i] * f[p[j]];
}
}
}
void initival(int n,int m)
{
memcpy(sumn,f,sizeof(sumn));
memcpy(summ,f,sizeof(summ));
for (int k:p)
{
for(int i=n/k;i>=0;i--)
sumn[i]+=sumn[i*k];
for(int i=m/k;i>=0;i--)
summ[i]+=summ[i*k];
}
}
signed main()
{
int MOD;
cin >> n >> m >> MOD;
Euler(max(n,m));
initival(n,m);
long long ans=0;
for (int i = 1; i <= min(n, m); i++)
{
ans += 1ll*sumn[i] * summ[i];
}
cout << ans % MOD;
return 0;
}
10.Array Equalizer
题目大意
有两个长度为\(n\)的数组\(a\)和\(b\),已知数组\(a\)和\(b\)除\(b_1\)以外的所有数,进行以下两种操作
1.选择一个\(i\) \((1\le i\le n)\),将所有\(a_k\)加\(1\),其中\(1\le i\times k\le n\)
2.选择一个\(i\) \((1\le i\le n)\),将所有\(a_k\)减\(1\),其中\(1\le i\times k\le n\)
有\(q\)组询问,每次输入一个\(b_1\),求把\(a\)变为\(b\)的最少操作次数
分析
记\(c_i=b_i-a_i\),将\(a\)操作成\(b\),即为将\(c\)操作成\(0\)
因为\(c_i\)必然在操作\(c_j\) \((j|i,j\ne i)\)时被被动操作
记\(f(i)\)表示主动对\(c_i\)的总操作为减\(f(i)\)(可能为负),即操作\(c_i\)的次数为\(|f(i)|\)
所以
\[f(i)={c_i-\sum_{d|i,d\le i}f(d)} \]移项得
\[f(i)+\sum_{d|i,d\ne i}f(d)=\sum_{d|i}f(d)=c_i \]代入莫反的式子得
\[f(i)=\sum_{d|i}\mu(\frac{i}{d})\cdot c_d \]于是总操作次数为
\[s=\sum_{i=1}^n|\sum_{d|i}\mu(\frac{i}{d})\cdot c_d| \]因为已知\(a\)和\(b\)除\(b_1\)以外的所有数,即知道\(c_2,c_3,……,c_n\)
可以考虑将不含\(c_1\)的项提前计算(多组数据……),再将\(c_1\)的贡献加上
\[s=\sum_{i=1}^n|\sum_{d|i,d\ne 1}\mu(\frac{i}{d})\cdot c_d+\mu(i)\cdot c_1| \]发现\(c_1\)系数为\(\mu(i)\),而\(\mu(i)\)只有\(-1,0,1\)三种取值
记
\[g(i)=\sum_{d|i,d\ne 1}\mu(\frac{i}{d})\cdot c_d \]考虑将\(g(i)\)按\(\mu(i)\)分成三类,每一类分别计算
若\(\mu(i)=0\),\(c_i\)未产生贡献,直接将\(|g(i)|\)累加进答案即可
否则\(c_i\)会对答案产生贡献
由于有绝对值
对于\(g(i)<-\mu(i)\cdot c_1\) ,\(|f(i)|=\mu(i)\cdot c_1-g(i)\)
否则,\(|f(i)|=g(i)-\mu(i)\cdot c_1\)
考虑将\(\mu(i)=1\),\(\mu(i)=-1\)的\(g(i)\)分别排序并处理前缀和\(sum\)
每次询问,二分查找小于\(-\mu(i)\cdot c_1\)的\(g(i)\)个数\(l\),那么
\[s+=(l\cdot\mu(i)\cdot c_1-sum[l])+((sum[cnt]-sum[l])-(cnt-l)\cdot\mu(i)\cdot c_1) \]其中\(l\cdot\mu(i)\cdot c_1-sum[l]\)为\(g(i)<-\mu(i)\cdot c_1\)部分对答案的贡献,
\((sum[cnt]-sum[l])-(cnt-l)\cdot\mu(i)\cdot c_1\)为\(g(i)\ge-\mu(i)\cdot c_1\)部分对答案的贡献
这样每次询问复杂度为\(O(\log n)\)
总复杂度为\(O(n+Q\cdot\log n)\)
AC·code
#include<bits/stdc++.h>
#define il inline
#define I il int
#define V il void
#define cs const
#define ri register
#define int long long
#define F(s) freopen(#s".in","r",stdin),freopen(#s".out","w",stdout);
using namespace std;
I rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
V wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
putchar(x%10+48);
}
cs int N=2e5+5;
int ss[N],cnt,mu[N],f[N];bool vs[N];
int pos[N],tp,neg[N],tn;
int fd,cha,n,q,a[N],b[N];
long long sumz,as,sp[N],sn[N];
V init(int n){
mu[1]=1;
for(ri int i=2;i<=n;++i){
if(!vs[i]) ss[++cnt]=i,mu[i]=-1;
for(ri int j=1;j<=cnt&&i*ss[j]<=n;++j){
vs[i*ss[j]]=1;
if(i%ss[j]==0) break;
mu[i*ss[j]]=-mu[i];
}
}
for(ri int i=2;i<=n;++i){
for(ri int j=i;j<=n;j+=i) f[j]+=mu[j/i]*(b[i]-a[i]);
(!mu[i])?sumz+=abs(f[i]):((~mu[i])?neg[++tn]=f[i]:pos[++tp]=f[i]);
}
sort(neg+1,neg+1+tn),sort(pos+1,pos+1+tp);
for(ri int i=1;i<=tn;++i) sn[i]=sn[i-1]+neg[i];
for(ri int i=1;i<=tp;++i) sp[i]=sp[i-1]+pos[i];
}
signed main(){
n=rd();for(ri int i=1;i<=n;++i) a[i]=rd();
for(ri int i=1;i<=n;++i) b[i]=rd();b[1]=0;
init(n),q=rd();while(q--){
cha=rd()-a[1],as=abs(cha);
fd=lower_bound(pos+1,pos+1+tp,cha)-pos-1;
as+=cha*fd-2*sp[fd]+sp[tp]-cha*(tp-fd);
fd=lower_bound(neg+1,neg+1+tn,-cha)-neg-1;
as+=-cha*fd-2*sn[fd]+sn[tn]+cha*(tn-fd);
wt(as+sumz),putchar('\n');
}
return 0;
}
11.LCMSUM - LCM Sum
题目大意
求\(\sum_{i=1}^n lcm(i,n)\) \((1\le n\le 10^6)\)
分析
暴力推式子
因为\(lcm(a,b)=\frac{a\cdot b}{\gcd(a,b)}\)
所以
\[\begin{align*} s&=\sum_{i=1}^n\frac{i\cdot n}{\gcd(i,n)}\\ &=n\cdot\sum_{i=1}^n\frac{i}{\gcd(i,n)}\\ &=n\cdot\sum_{d|n}\sum_{i=1}^{\frac{n}{d}}i\cdot[\gcd(i,\frac{n}{d})=1]\\ &=n\cdot\sum_{d|n}\sum_{i=1}^{\frac{n}{d}}i\cdot\sum_{g|\gcd(i,\frac{n}{d})}\mu(g)\\ \end{align*} \]由于
\[\begin{align*} \sum_{i=1}^{n'}i\cdot\sum_{g|gcd(i,n')}\mu(g)&=\sum_{g|n'}\mu(g)\cdot\sum_{i=1}^{\frac{n'}{t}}i\cdot t\\ &=\sum_{g|n'}\mu(g)\cdot g\cdot\sum_{i=1}^{\frac{n'}{g}}i\\\ &=\sum_{g|n'}\mu(g)\cdot\frac{n'^2+n'\cdot g}{2\cdot g} \end{align*} \]所以
\[\begin{align*} s&=n\cdot\sum_{d|n}\sum_{g|\frac{n}{d}}\mu(g)\cdot\frac{(\frac{n}{d})^2+\frac{n\cdot g}{d}}{2\cdot g}\\ &=n\cdot\sum_{d|n}\sum_{t|d}\mu(t)\cdot\frac{d^2+d\cdot t}{2\cdot t}\\ &=\frac{n}{2}\sum_{d|n}\sum_{t|d}d\cdot(\mu(t)\cdot\frac{d}{t}+\mu(t))\\ &=\frac{n}{2}\cdot\sum_{d|n}d\cdot(\sum_{t|d}\mu(t)\cdot\frac{d}{t}+\sum_{t|d}\mu(t)) \end{align*} \]然后我试图继续用\(\mu\)写,推了一些奇怪的式子(甚至推回去了),发现比暴力还劣TAT
事实上,这里离我们需要的式子很近了QAQ(所以我怎么推到这里的)
划重点,由莫比乌斯反演公式\(\mu*I=\epsilon\),\(\mu*id=\varphi\)得
所以
\[\begin{align*} s&=\frac{n}{2}\sum_{d|n}d\cdot(\varphi(d)+\epsilon(d))\\ &=n\cdot(\frac{1}{2}+\sum_{d|n}\frac{\varphi(d)\cdot d}{2})\\ &=n\cdot(1+\sum_{d|n,d\ne 1}\frac{\varphi(d)\cdot d}{2}) \end{align*} \]\(ps:\)其中\(\frac{1}{2}\)是对于\(\epsilon(d)\)的特判(\(\epsilon(d)=[d=1]\)),为了方便处理,直接把\(1\)提出来了
这样就可以预处理\(\sum_{d|n}\frac{\varphi(d)\cdot d}{2}\),复杂度为调和级数,询问\(O(1)\)
总复杂度\(O(n\log n+Q)\)
大眼观察法
对于前面推的
\[s=n\cdot\sum_{d|n}\sum_{i=1}^\frac{n}{d}i\cdot[\gcd(i,\frac{n}{d})=1] \]用\(d\)替换\(\frac{n}{d}\),得
\[s=n\cdot\sum_{d|n}\sum_{i=1}^di\cdot[\gcd(i,d)=1] \]发现\(\sum_{i=1}^di\cdot[\gcd(i,d)]=1\)恰好表示\([1,d]\)中与\(d\)互质的数的和
有一个神奇性质,\([1,d]\)中与\(d\)互质的数的和等于\(\frac{\varphi(d)\cdot d}{2}\) (当\(d=1\)时为\(1\))
证明:
当\(d>2\)时,与\(d\)互质的数总是成对出现,且有\(\frac{\varphi(d)}{2}\)对。具体来说,若\(i\perp d\),则必然有\(d-i\perp d\)
,且它们的和为\(d\)。于是\(d\)互质的数的和等于\(d\cdot\frac{\varphi(d)}{2}=\frac{\varphi(d)\cdot d}{2}\),等式成立
当\(d=2\)时,与\(d\)互质的数只有\(1\),与\(d\)互质的数的和即为\(1\),恰好等于\(\frac{\varphi(d)\cdot d}{2}\),上式仍成立
证毕
于是
\[s=\begin{cases} 1 &d=1\\ n\cdot\sum_{d|n}\frac{\varphi(d)\cdot d}{2} &d>1 \end{cases} \]其实就等于我们之前推出的式子qwq~
\(ps\):所以看到\([\gcd(p,q)=1]\)不仅要想到\(\mu\),还要想到\(\varphi\),有时会好推很多qnq
线性筛做法
oi-wiki上给出了一种\(O(n+T)\)的线性筛做法(做法神奇)
受等差数列求和的公式启发,我们将
\[\begin{align*} s&=\sum_{i=1}^n\frac{i\cdot n}{\gcd(i,n)} =\sum_{i=n}^1\frac{i\cdot n}{\gcd(i,n)} \end{align*} \]两式相加,得
\[2\cdot s=\sum_{i=1}^n\frac{i\cdot n}{\gcd(i,n)}+\sum_{i=n}^1\frac{i\cdot n}{\gcd(i,n)} \]于是(这不废话吗)
为方便后面操作,我们将\(i=n\)的这一项单独提出来,得
\[s=n+\frac{1}{2}\cdot(\sum_{i=1}^{n-1}\frac{i\cdot n}{\gcd(i,n)}+ \sum_{i=n-1}^1\frac{i\cdot n}{\gcd(n-i,n)}) \]由于\(\gcd(i,n)=\gcd(n-i,n)\)(想一下辗转相减法),原式可化为
可将分母相同的项合并,得
\[\begin{align*} s&=n+\frac{1}{2}\cdot\sum_{i=1}^{n-1}\frac{i\cdot n+(n-i)\cdot n}{\gcd(i,n)}\\ &=n+\frac{1}{2}\cdot\sum_{i=1}^{n-1}\frac{n^2}{\gcd(i,n)} \end{align*} \]再把\(n\)并进去
\[s=\frac{n}{2}+\frac{1}{2}\cdot\sum_{i=1}^n\frac{n^2}{\gcd(i,n)} \]可以将相同的\(\gcd(i,n)\)一起计算,即
\[\begin{align*} s&=\frac{n}{2}+\frac{1}{2}\cdot\sum_{d|n}\sum_{i=1}^{\frac{n}{d}}\frac{n^2}{d}\cdot[\gcd(i,\frac{n}{d})=1]\\ \end{align*} \]对于\([\gcd(i,\frac{n}{d})=1]\),产生贡献的\(i\)有\(\varphi(\frac{n}{d})\)个,故答案为
\[s=\frac{n}{2}+\frac{1}{2}\cdot\sum_{d|n}\frac{n^2\cdot\varphi(\frac{n}{d})}{d} \]提出\(n\),令\(d=\frac{n}{d}\)代入得
\[\begin{align*} s&=\frac{n}{2}\cdot(1+\sum_{d|n}\varphi(d)\cdot d)\\ \end{align*} \]到这里,就是前面推得的式子了
考虑进一步优化
设\(g(n)=\sum_{d|n}d\cdot\varphi(d)\)
发现\(g\)为积性函数:
当\(p\perp q\)时,
\[\varphi(p\cdot q)=\varphi(p)\cdot\varphi(q) \]则
\[\begin{align*} g(p\cdot q)&=\sum_{d|p\cdot q}d\cdot \varphi(d)\\ &=(\sum_{d|p}d\cdot\varphi(d))\cdot(\sum_{d|q}d\cdot\varphi(d))\\ &=g(p)\cdot g(q) \end{align*} \]可以线性筛出
先考虑\(g(p_j^k)\)的值,\(p_j^k\)的约数只有\(p_j^0,p_j^1,\)……\(,p_j^k\),因此
\[g(p_j^k)=\sum_{w=0}^kp_j^w\cdot\varphi(p_j^w) \]又有\(\varphi(p_j^w)=p_j^{w-1}\cdot(p_j-1)\),则
\[g(p_j^k)=\sum_{w=0}^kp_j^{2w-1} \]那么
\[g(p_j^{k+1})=g(p_j^k)+p_j^{2k+1}\cdot(p_j-1) \]于是对于线性筛中\(g(i\cdot p_j)\) \((p_j|i)\),令\(i=q\cdot p_j^w\)\((\gcd(q,p_j)=1)\),可得
\[\begin{align*} g(i\cdot p_j)&=g(q)\cdot g(p_j^{w+1})\\ g(i)&=g(q)\cdot g(p_j^w) \end{align*} \]上式减下式,得
\[g(i\cdot p_j)-g(i)=g(q)\cdot p_j^{2w+1}\cdot(p_j-1) \]用\(\frac{i}{p_j}\)替换\(i\),有
\[g(i)-g(\frac{i}{p_j})=g(q)\cdot p_j^{2w-1}\cdot(p_j-1) \]因此
\[g(i\cdot p_j)=g(i)+(g(i)-g(\frac{i}{p_j}))\cdot p_j^2 \]于是,线性筛的式子为
\[g(i\cdot q)=\begin{cases} p\cdot(p-1)+1 &(i=1)\\ g(i)+(g(i)-g(\frac{i}{p}))\cdot p^2 &(p\vert i) \end{cases} \]复杂度\(O(n+Q)\)
O(n log n+Q)代码
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
#define int long long//记得开long long!
using namespace std;
cs int N=1e6;
int ss[N],fi[N+1],f[N+1],cnt;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
putchar(x%10+48);
}
il void init(int n){
for(ri int i=2;i<=n;++i){
if(!fi[i]) fi[i]=i-1,ss[++cnt]=i;
for(ri int j=1;j<=cnt&&ss[j]*i<=n;++j){
if(i%ss[j]) fi[i*ss[j]]=fi[i]*fi[ss[j]];
else {fi[i*ss[j]]=fi[i]*ss[j]; break;}
}
}
for(ri int i=2;i<=n;++i){
for(ri int j=i;j<=n;j+=i){
f[j]+=fi[i]*i/2;
}
}
return;
}
signed main(){
int t=rd(),n;init(N);
while(t--) n=rd(),wt(n*(1+f[n])),putchar('\n');
return 0;
}
O(n+Q)代码
#include<bits/stdc++.h>
#define il inline
#define I il int
#define V il void
#define ri register
#define cs const
#define int long long
#define ss2 ss[j]*ss[j]
using namespace std;
I rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
V wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
putchar(x%10+48);
}
cs int N=1e6+5;
int ss[N],g[N],cnt;
bool vs[N];
V init(int n){
g[1]=1;
for(ri int i=2;i<=n;++i){
if(!vs[i]) ss[++cnt]=i,g[i]=(i-1)*i+1;
for(ri int j=1;j<=cnt&&ss[j]*i<=n;++j){
vs[i*ss[j]]=1;
if(i%ss[j]==0){
g[i*ss[j]]=(g[i]-g[i/ss[j]])*ss2+g[i];
break;
}
g[i*ss[j]]=g[i]*g[ss[j]];
}
}
}
int t,n;
signed main(){
t=rd(),init(N-5);
while(t--){
n=rd();
wt((n*g[n]+n)/2);
putchar('\n');
}
return 0;
}