莫反的一些练习题(2)

莫比乌斯反演
莫反的一些练习题(1)


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})\) (这不是四倍经验吗)

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 \]

  • 开始推式子

\[\begin{align*} s&=\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^n\tau(i)\cdot\tau(j)\cdot\tau(d)\cdot[\gcd(i,j)=d] \\&=\sum_{d=1}^n\tau(d)\cdot\sum_{i=1}^n\sum_{j=1}^m\tau(i)\cdot\tau(j)\cdot[gcd(i,j)=d] \\&=\sum_{d=1}^n\tau(d)\cdot\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\tau(i\cdot d)\cdot\tau(j\cdot d)\cdot[\gcd(i,j)=1] \\&=\sum_{d=1}^n\tau(d)\cdot\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\tau(i\cdot d)\cdot\tau(j\cdot d)\cdot\sum_{g|\gcd(i,j)}\mu(g) \\&=\sum_{d=1}^n\tau(d)\cdot\sum_{g=1}^{\lfloor\frac{n}{d}\rfloor}\mu(g)\cdot\sum_{i=1}^{\lfloor\frac{n}{d\cdot g}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d\cdot g}\rfloor}\tau(i\cdot d\cdot g)\cdot\tau(j\cdot d\cdot g) \end{align*} \]

\(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)\)

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*} &\sum_{t|d}\mu(t)\cdot \frac{d}{t}=\varphi(d)\\ &\sum_{t|d}\mu(t)=\epsilon(d) \end{align*} \]

所以

\[\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)} \]

于是(这不废话吗)

\[s=\frac{1}{2}\cdot(\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)\)(想一下辗转相减法),原式可化为

\[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)}) \]

可将分母相同的项合并,得

\[\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;
} 

edit

posted @ 2023-01-12 21:54  雨夜风月  阅读(35)  评论(0编辑  收藏  举报