莫比乌斯

YY的GCD

\[\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)\in prime] \]

\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;=\sum_{p\in prime}\sum_{i=1}^{\left\lfloor\dfrac{n}{p}\right\rfloor}\sum_{j=1}^{\left\lfloor\dfrac{m}{p}\right\rfloor}\sum_{d|gcd(i,j)}{\mu(d)}\)

\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;=\sum_{p\in prime}\sum_{d=1}^{min(n/p,m/p)}\mu(d)\:*\:{\left\lfloor\dfrac{n}{pd}\right\rfloor}\:*\:{\left\lfloor\dfrac{m}{pd}\right\rfloor}\)

\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;=\sum_{k=1}^{n}f(k)\:*\:{\left\lfloor\dfrac{n}{k}\right\rfloor}\:*\:{\left\lfloor\dfrac{m}{k}\right\rfloor}\)

\[f(x) = \sum_{p\in prime} \mu(\frac{x}{p}) \]

\[f(x) = \begin{cases}\mu(1)&x=1\\ \mu(i)&i \mod y= 0\\ -f(i) + \mu(i)& i \mod y \neq 0\end{cases} \]

数论分块 + 前缀和

#include<cstdio>
#define Starseven main
#define ll long long
namespace lyt {
	void read(int &x){
	char ch=getchar();int re=0,op=1;
	while(ch<'0'||ch>'9'){if(ch=='-') op=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){re=(re<<3)+(re<<1)+ch-'0';ch=getchar();}
	x = re * op;
	return ;
	}
	void read(long long &x){
	char ch=getchar();long long re=0,op=1;
	while(ch<'0'||ch>'9'){if(ch=='-') op=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){re=(re<<3ll)+(re<<1ll)+ch-'0';ch=getchar();}
	x = re * op;
	return ;
	}
	void write(int x){
		if(x<0){putchar('-');x=-x;}
		if(x>9) write(x/10);
		putchar(x%10+'0');
		return ;
	}//记得自己加空格和换行 
	void write(long long x){
		if(x<0){putchar('-');x=-x;}
		if(x>9) write(x/10);
		putchar(x%10+'0');
		return ;
	}//记得自己加空格和换行
	int max(int x,int y){return x<y?y:x;}
	int min(int x,int y){return x<y?x:y;}
	int abs(int x){return x<0?-x:x;}
	long long max(long long x,long long y){return x<y?y:x;}
	long long min(long long x,long long y){return x<y?x:y;}
	long long abs(long long x){return x<0?-x:x;}
	double abs(double x){return x<0?-x:x;}
}using namespace lyt;
const int maxn = 1e7;
int f[maxn + 20], prime[1000000 + 20], num, mu[maxn + 20];
bool vis[maxn + 20];

void Init() {
	mu[1] = 1;
	for (int i = 2; i <= maxn; i++) {
		if (vis[i] == false) {
			prime[++num] = i;
			mu[i] = -1;
			f[i] = 1;
		}
		for (int j = 1; j <= num && prime[j] * i <= maxn; j++) {
			int x = prime[j] * i;
			vis[x] = true;
			if(i % prime[j] == 0) {
				f[x] = mu[i];
				mu[x] = 0;
				break;
			}
			else {
				f[x] = -f[i] + mu[i];
				mu[x] = -mu[i];
			}
		}
	}
	for (int i = 1; i <= maxn; i++) {
		f[i] += f[i - 1];
	}
}

int Starseven(void) {
	int t;
	read(t);
	Init();
	while(t--) {
		int n, m;
		read(n);
		read(m);
		if (n > m) n ^= m ^= n ^= m;
		ll ans = 0;
		for (int i = 1, j; i <= n; i = j + 1) {
			j = min(n / (n / i), m / (m / i));
			ans += 1ll * (f[j] - f[i - 1]) * (n / i) * (m / i);
		}
		write(ans);puts("");
	}
	return 0;	
}

约数个数和

闲话:

为什么大家做题是越做越熟练,我是越做越茫然?

\[\begin{aligned} \sum_{i=1}^{n}\sum_{j=1}^{m}d(ij) & = \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] \\ & = \sum_{x=1}^{n}\sum_{y=1}^{m}\:*\:{\left\lfloor\dfrac{n}{x}\right\rfloor}\:*\:{\left\lfloor\dfrac{m}{y}\right\rfloor}[gcd(x,y)=1] \\ & = \sum_{x=1}^{n}\sum_{y=1}^{m}{\left\lfloor\dfrac{n}{x}\right\rfloor}\:*\:{\left\lfloor\dfrac{m}{y}\right\rfloor}\:*\:\sum_{t|gcd(x,y)}\mu(t) \\ & = \sum_{t=1}^{min(n,m)}\mu(t)\times \sum_{x=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\sum_{y=1}^{\left\lfloor\dfrac{m}{t}\right\rfloor}\left\lfloor\dfrac{n}{xt}\right\rfloor\:*\:\left\lfloor\dfrac{m}{xt}\right\rfloor \\ & = \sum_{t=1}^{min(n,m)}\mu(t)\times \sum_{x=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\left\lfloor\dfrac{\left\lfloor\dfrac{n}{x}\right\rfloor}{t}\right\rfloor\times \sum_{x=1}^{\left\lfloor\dfrac{m}{t}\right\rfloor}\left\lfloor\dfrac{\left\lfloor\dfrac{m}{x}\right\rfloor}{t}\right\rfloor \end{aligned} \]

\[\text{莫比乌斯反演的“套路”} \text{设}f(x) = \sum_{i=1}^{x}{\left\lfloor\dfrac{x}{i}\right\rfloor} \]

\[ans=\sum_{t=1}^{min(n,m)}\mu(t)\times f(\left\lfloor\dfrac{n}{t}\right\rfloor)\times f(\left\lfloor\dfrac{m}{t}\right\rfloor) \]

\(\mu\)用前缀和

而f函数可以用数论分块\(O(\sqrt{N})\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
 
const int maxn=50000+5;
 
int n,m,cas,cnt,mu[maxn],pri[maxn],vis[maxn];
long long ans,f[maxn];
 
inline long long calc(int x){
    long long res=0;
    for(int i=1,r;i<=x;i=r+1){
        r=x/(x/i);
        res+=(x/i)*(r-i+1);
    }
    return res;
}
 
inline void prework(void){
    mu[1]=1;
    for(int i=2;i<=50000;i++){
        if(!vis[i])
            vis[i]=1,pri[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&i*pri[j]<=50000;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0){
                mu[i*pri[j]]=0;break;
            }
            mu[i*pri[j]]=-mu[i];
        }
    }
    for(int i=1;i<=50000;i++) mu[i]+=mu[i-1],f[i]=calc(i);
}
 
signed main(void){
    scanf("%d",&cas);prework();
    while(cas--){
        scanf("%d%d",&n,&m);
        if(n>m) swap(n,m);ans=0;
        for(int i=1,r;i<=n;i=r+1){
            r=min(n/(n/i),m/(m/i));
            ans+=f[n/i]*f[m/i]*(mu[r]-mu[i-1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

我裂开来

Crash的数字表格

\[ans=\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j) \]

\[\begin{aligned} \sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j) & = \sum_{t=1}^{min(n,m)}t\times \sum_{i=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\sum_{j=1}^{\left\lfloor\dfrac{m}{t}\right\rfloor}(i\times j)[gcd(i,j=1)] \\ & = \sum_{t=1}^{min(n,m)}t\times \sum_{i=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\sum_{j=1}^{\left\lfloor\dfrac{m}{t}\right\rfloor}i\times j\times(\sum_{h|gcd(i,j)}\mu(h)) \\ & = \sum_{t=1}^{min(n,m)}t\times\sum_{h=1}^{min(\left\lfloor\dfrac{m}{t}\right\rfloor,\left\lfloor\dfrac{n}{t}\right\rfloor)}\mu(h)\times h^2 \times\sum_{x=1}^{\left\lfloor\dfrac{n}{th}\right\rfloor}x \times \sum_{y=1}^{\left\lfloor\dfrac{m}{th}\right\rfloor}y \end{aligned} \]

然后我们观察式子

\[ans=\sum_{t=1}^{min(n,m)}t\times\sum_{h=1}^{min(\left\lfloor\dfrac{m}{t}\right\rfloor,\left\lfloor\dfrac{n}{t}\right\rfloor)}\mu(h)\times h^2 \times\sum_{x=1}^{\left\lfloor\dfrac{n}{th}\right\rfloor}x \times \sum_{y=1}^{\left\lfloor\dfrac{m}{th}\right\rfloor}y \]

我们就可以设

\[behind(x,y)=\sum_{h=1}^{min(x,y)}\mu(h)\times h^2 \times \sum_{i=1}^{\left\lfloor\dfrac{x}{h}\right\rfloor}i \times \sum_{j=1}^{\left\lfloor\dfrac{y}{h}\right\rfloor}j \]

这个式子是不是可以预处理一下?

就是三个前缀和拼凑!

然后数论分块可以求出\(behind(x,y)\)的值!

\[ans=\sum_{t=1}^{min(n,m)}t\times behind(\left\lfloor\dfrac{n}{t}\right\rfloor,\left\lfloor\dfrac{m}{t}\right\rfloor) \]

又是一个数论分块!

所以就解决了

#include<cstdio>
#define Starseven main
#define ll long long
namespace lyt {
	void read(int &x){
	char ch=getchar();int re=0,op=1;
	while(ch<'0'||ch>'9'){if(ch=='-') op=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){re=(re<<3)+(re<<1)+ch-'0';ch=getchar();}
	x = re * op;
	return ;
	}
	void read(long long &x){
	char ch=getchar();long long re=0,op=1;
	while(ch<'0'||ch>'9'){if(ch=='-') op=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){re=(re<<3ll)+(re<<1ll)+ch-'0';ch=getchar();}
	x = re * op;
	return ;
	}
	void write(int x){
		if(x<0){putchar('-');x=-x;}
		if(x>9) write(x/10);
		putchar(x%10+'0');
		return ;
	}//记得自己加空格和换行 
	void write(long long x){
		if(x<0){putchar('-');x=-x;}
		if(x>9) write(x/10);
		putchar(x%10+'0');
		return ;
	}//记得自己加空格和换行
	int max(int x,int y){return x<y?y:x;}
	int min(int x,int y){return x<y?x:y;}
	int abs(int x){return x<0?-x:x;}
	long long max(long long x,long long y){return x<y?y:x;}
	long long min(long long x,long long y){return x<y?x:y;}
	long long abs(long long x){return x<0?-x:x;}
	double abs(double x){return x<0?-x:x;}
	void swap(int &a,int &b) {a ^= b ^= a ^= b;}
	void swap(long long &a,long long &b) {a ^= b ^= a ^= b;}
}using namespace lyt;
const int maxn = 1e7;
const ll mod = 20101009;

int prime[maxn + 20], num;
ll mu[maxn + 20], add[maxn + 20];
bool vis[maxn + 20];

void Init() {
	mu[1] = 1;
	for (int i = 2; i <= maxn; i++) {
		if (!vis[i]) {
			prime[++num] = i;
			mu[i] = -1;
		}
		for (int j = 1; j <= num && prime[j] * i <= maxn; j++) {
			int x = i * prime[j];
			vis[x] = true;
			if (i % prime[j] == 0) {
				mu[x] = 0;
				break;
			}
			mu[x] = -mu[i];
		}
	}
	for (int i = 1; i <= maxn; i++) {
		mu[i] = mu[i] * (ll)i * (ll)i % mod;
		mu[i] += mu[i - 1];
		if(mu[i] > mod) mu[i] %= mod;
		add[i] = add[i - 1] + i;
		if(add[i] > mod) add[i] %= mod;
	}
	return ;
}

ll Behind(ll x, ll y) {
	ll re = 0;
	for (ll h = 1; h <= min(x, y); h++) {
		ll g = min(x / (x / h), y / (y / h));
		ll a = x / h, b = y / h;
		re += (mu[g] - mu[h - 1]) % mod * add[a] % mod * add[b] % mod;
		if(re > mod) re %= mod;
		h = g;
	}
	re += mod;
	return (re % mod);
}

int Starseven(void) {
	ll n, m;
	read(n);
	read(m);
	if(n > m) swap(n, m);
	Init(); 
	ll ans = 0;
	for (ll t = 1; t <= n; t++) {
		ll x = min(n / (n / t), m / (m / t));
		ll behind = Behind(n / t, m / t);
		ans = (ans + (x - t + 1) * (x + t) / 2 * behind) % mod;
		t = x;
	}
	write(ans);puts("");
	return 0;	
}

数表

\[ans=\sum_{i=1}^{n}\sum_{j=1}^{m}[a\geq\sum_{d|gcd(i,j)}d]\times\sum_{d|gcd(i,j)}d \]

我们假设

\[G(n,m)=\sum_{d|gcd(n,m)}d \]

\[n \leq m \]

\[\begin{aligned} G(n,m) & = \sum_{d|gcd(n,m)}d \\ & = \sum_{d=1}^{n}d\times[d|gcd(n,m)] \\ & = \sum_{d=1}^nd\times[mn=\left\lfloor\dfrac{nm}{d^2}\right\rfloor\times d^2] \end{aligned} \]

这个思路有点难走,那我们换个思路

\[ans=(\sum_{i=1}^n\sum_{j=1}^m\sum_{d|gcd(i,j)}d)-(\sum_{i=1}^n\sum_{j=1}^m[a<\sum_{d|gcd(i,j)}d]\times\sum_{d|gcd(i,j)}d) \]

我们设前面的式子为sum(n,m),则

\[\begin{aligned} sum(n,m) & =\sum_{i=1}^n\sum_{j=1}^m\sum_{d|gcd(i,j)}d \\ & =\sum_{d=1}^nd\times \left\lfloor\dfrac{n}{d}\right\rfloor\times\left\lfloor\dfrac{m}{d}\right\rfloor \end{aligned} \]

这个可以用数论分块

然后我们发现其实后面的式子和上面没有用减法原理的式子一样,所以失败


这个时候,我们发现我们最开始枚举的东西就是除数函数!

其实我没有发现,是看题解才知道的

所以我们可以写出sum(n,m)的另一个版本

\[\begin{aligned} sum(n,m) & = \sum_{i=1}^n\sum_{j=1}^m\sigma(gcd(i,j)) \\ & = \sum_{t=1}^{n}\sigma(t)\sum_{i=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\sum_{j=1}^{\left\lfloor\dfrac{m}{t}\right\rfloor}[gcd(i,j)=1] \\ & = \sum_{t=1}^{n}\sigma(t)\sum_{i=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\sum_{j=1}^{\left\lfloor\dfrac{m}{t}\right\rfloor}\sum_{d|gcd(i,j)}\mu(d) \\ & = \sum_{t=1}^{n}\sigma(t)\sum_{d=1}^{\left\lfloor\dfrac{n}{t}\right\rfloor}\mu(d)\left\lfloor\dfrac{n}{dt}\right\rfloor\left\lfloor\dfrac{m}{dt}\right\rfloor \\ & =\sum_{T=1}^{n}\left\lfloor\dfrac{n}{T}\right\rfloor\left\lfloor\dfrac{m}{T}\right\rfloor\sum_{d|T}\sigma(d)\mu(\frac{T}{d}) \end{aligned} \]

此时,前面的求和可以分块\(O(n + m)\),后面的是个积性函数,可以预处理,然后就是O(1)

但是,不是每个东西都对答案有贡献,所以我们要排除掉没有贡献的.

然后就需要维护答案,想到维护这个词,在看一下式子,

\[\color{red}\text{单点修改,区间查询} \]

这不就是树状数组吗?

所以我鸽了……


P4450 双亲数

题解


P4449 于神之怒加强版

题解


P3911 最小公倍数之和

题解


P6156 简单题

题解


P1891 疯狂 LCM

题解

posted @ 2020-08-25 16:10  starseven  阅读(196)  评论(0编辑  收藏  举报