莫比乌斯反演、容斥 题集

洛谷 P2257

题意

x ∈ [ 1 , N ] x \in[1,N] x[1,N], y ∈ [ 1 , M ] y \in [1,M] y[1,M],且 ( x , y ) = 质 数 (x,y) = 质数 (x,y)=的点对个数

F ( N , M ) = ∑ p    i s    p r i m e ∑ i = 1 N ∑ j = 1 M [ ( i , j ) = p ] = ∑ p    i s    p r i m e ∑ i = 1 ⌊ N p ⌋ ∑ j = 1 ⌊ M p ⌋ [ ( i , j ) = 1 ] = ∑ p    i s    p r i m e ∑ d = 1 min ⁡ ( ⌊ N p ⌋ , ⌊ M p ⌋ ) μ ( d ) ⋅ ⌊ N p d ⌋ ⋅ ⌊ M p d ⌋      ( 令 k = p d ) = ∑ k = 1 min ⁡ ( N , M ) ⌊ N k ⌋ ⋅ ⌊ M k ⌋ ⋅ ∑ d ∣ k μ ( d ) ⋅ [ k / d    i s    p r i m e ] \begin{aligned} F(N,M) &= \sum_{p\;is\;prime}{\sum_{i=1}^{N}{\sum_{j=1}^{M}{[(i,j) = p]}}}\\ &=\sum_{p\;is\;prime}{\sum_{i=1}^{\lfloor\frac{N}{p}\rfloor}{\sum_{j=1}^{\lfloor\frac{M}{p}\rfloor}{[(i,j) = 1]}}}\\ &=\sum_{p\;is\;prime}{\sum_{d=1}^{\min(\lfloor\frac{N}{p}\rfloor,\lfloor\frac{M}{p}\rfloor)}{\mu(d)\cdot \lfloor\frac{N}{pd}\rfloor \cdot \lfloor\frac{M}{pd}\rfloor}}\;\; (令k = pd)\\ &=\sum_{k=1}^{\min(N,M)}{\lfloor\frac{N}{k}\rfloor\cdot\lfloor\frac{M}{k}\rfloor\cdot\sum_{d | k}{\mu(d)\cdot [k/d\;is\;prime]}} \end{aligned} F(N,M)=pisprimei=1Nj=1M[(i,j)=p]=pisprimei=1pNj=1pM[(i,j)=1]=pisprimed=1min(pN,pM)μ(d)pdNpdM(k=pd)=k=1min(N,M)kNkMdkμ(d)[k/disprime]

后半部分可以预处理,前半部分整除分块。

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 10000000 + 500;
typedef long long ll;
bool used[maxn];
ll g[maxn];
int mu[maxn];
vector<int> prime;
void seive(){
    mu[1] = 1;
    for (int i=2;i<maxn;i++){
        if (!used[i]){
            prime.push_back(i);
            mu[i] = -1;
        }
        for (int j=0;j<prime.size();j++){
            ll nxt = 1ll  * prime[j] * i;
            if (nxt >=maxn)break;
            used[nxt] = 1;
            if (i % prime[j]){
                mu[nxt] = -mu[i];
            }else{
                mu[nxt] = 0;
                break;
            }
        }
    }
    for (int i=0;i<prime.size();i++){
        int tmp = prime[i];
        for (int j = tmp;j < maxn;j += tmp){
            g[j] += mu[j/tmp];
        }
    }
    for (int i=1;i<maxn;i++){
        g[i] += g[i-1];
    }
}
ll work(int n,int m){
    int top = min(n,m);
    ll ans = 0;
    for (int i=1;i<=top;){
        int ii = min((m/(m/i)),(n/(n/i)));
        ans += 1ll * (n/i) * (m/i) * (g[ii] - g[i-1]);
        i = ii + 1;
    }
    return ans;
}
int main(){
    seive();
    int T;
    scanf("%d",&T);
    while (T--){
        int n,m;
        scanf("%d%d",&n,&m);
        printf("%lld\n",work(n,m));
    }
    return 0;
}

洛谷 P2522

题意

x ∈ [ a , b ] x\in[a,b] x[a,b], y ∈ [ c , d ] y\in[c,d] y[c,d],且 ( x , y ) = k (x,y) =k (x,y)=k的点对数量。

F ( N , M ) F(N,M) F(N,M)表示 x ∈ [ 1 , N ] x\in[1,N] x[1,N], y ∈ [ 1 , M ] y\in[1,M] y[1,M],且 ( x , y ) = k (x,y) = k (x,y)=k的点对数量。

A n s = F ( b , d ) − F ( a − 1 , d ) − F ( b , c − 1 ) + F ( a − 1 , c − 1 ) Ans = F(b,d) - F(a-1,d) - F(b,c-1) + F(a-1,c-1) Ans=F(b,d)F(a1,d)F(b,c1)+F(a1,c1)
F ( N , M ) = ∑ i = 1 N ∑ j = 1 M [ ( x , y ) = k ] = ∑ i = 1 ⌊ N k ⌋ ∑ j = 1 ⌊ M k ⌋ [ ( x , y ) = 1 ] = ∑ d = 1 min ⁡ ( ⌊ N k ⌋ , ⌊ M k ⌋ ) μ ( d ) ⋅ ⌊ N k d ⌋ ⋅ ⌊ M k d ⌋ \begin{aligned} F(N,M) &amp;= \sum_{i=1}^{N}{\sum_{j=1}^{M}{[(x,y) = k]}}\\ &amp;= \sum_{i=1}^{\lfloor\frac{N}{k}\rfloor}{\sum_{j=1}^{\lfloor\frac{M}{k}\rfloor}{[(x,y) = 1]}}\\ &amp;= \sum_{d=1}^{\min(\lfloor\frac{N}{k}\rfloor,\lfloor\frac{M}{k}\rfloor)}{\mu(d)\cdot \lfloor\frac{N}{kd}\rfloor\cdot\lfloor\frac{M}{kd}\rfloor} \end{aligned} F(N,M)=i=1Nj=1M[(x,y)=k]=i=1kNj=1kM[(x,y)=1]=d=1min(kN,kM)μ(d)kdNkdM
预处理 μ \mu μ,直接整除分块做。

Code:

#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 1e5+100;
typedef long long ll;
bool used[maxn];
vector<int> prime;
ll mu[maxn];
void sieve(){
    mu[1] = 1;
    for (int i=2;i<maxn;i++){
        if(!used[i]){
            prime.push_back(i);
            mu[i] = -1;
        }
        for (int j = 0;j<prime.size();j++){
            long long nxt = 1ll* prime[j] * i;
            if(nxt >= maxn)break;
            used[nxt] = 1;
            if (i % prime[j] == 0){
                mu[nxt] = 0;
                break;
            }else{
                mu[nxt] = -mu[i];
            }
        }
    }
    for (int i=2;i<maxn;i++){
        mu[i] += mu[i-1];
    }
}

ll work(int n,int m){
    ll ans = 0;
    int top = min(n,m);
    for (int i=1;i<=top;){
        int ni = (n/(n/i));
        int mi = (m/(m/i));
        int lasti = min(ni,mi);
        ans += 1ll * (mu[lasti] - mu[i-1]) * (n/i) * (m/i);
        i = lasti+1;
    }
    return ans;
}
int main(){
    sieve();
    int T;
    scanf("%d",&T);
    for (int Case = 1;Case <= T;Case ++){
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        ll ans = work(b/k,d/k) - work(b/k,(c-1)/k) - work((a-1)/k,d/k) + work((a-1)/k,(c-1)/k);
        printf("%lld\n",ans);		
    }
    return 0;
}

HDU 1695

题意

x ∈ [ 1 , b ] x\in[1,b] x[1,b], y ∈ [ 1 , d ] y\in[1,d] y[1,d],且 ( x , y ) = k (x,y) = k (x,y)=k的点对个数。

即上题中的 F ( N , M ) F(N,M) F(N,M)

Code:

#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 1e5+100;
typedef long long ll;
bool used[maxn];
vector<int> prime;
ll mu[maxn];
void sieve(){
    mu[1] = 1;
    for (int i=2;i<maxn;i++){
        if(!used[i]){
            prime.push_back(i);
            mu[i] = -1;
        }
        for (int j = 0;j<prime.size();j++){
            long long nxt = 1ll* prime[j] * i;
            if(nxt >= maxn)break;
            used[nxt] = 1;
            if (i % prime[j] == 0){
                mu[nxt] = 0;
                break;
            }else{
                mu[nxt] = -mu[i];
            }
        }
    }
}
ll work(int n,int m){
    ll ans = 0;
    int top = min(n,m);
    for (int i=1;i<=top;i++){
        ans += 1ll * mu[i] * (n/i) * (m/i);
    }
    return ans;
}
int main(){
    sieve();
    int T;
    scanf("%d",&T);
    for (int Case = 1;Case <= T;Case ++){
        int a,b,n,m,k;
        scanf("%d%d%d%d%d",&a,&n,&b,&m,&k);
        if(k == 0){
            printf("Case %d: 0\n",Case);
            continue;
        }
        n/=k;
        m/=k;
        printf("Case %d: %lld\n",Case,work(n,m) - work(min(n,m),min(n,m))/2);
    }
    return 0;
}

POJ 3904

题意

给出一个数组 a a a,从中选取四个数 a 1 , a 2 , a 3 , a 4 a1,a2,a3,a4 a1,a2,a3,a4,且 ( a 1 , a 2 , a 3 , a 4 ) = 1 (a1,a2,a3,a4) = 1 (a1,a2,a3,a4)=1求方案数。

A n s = ∑ i , j , k , l ∈ [ 1 , N ] [ ( a [ i ] , a [ j ] , a [ k ] , a [ l ] ) = 1 ] = ∑ d = 1 M a x μ ( d ) ⋅ C c n t [ d ] 4 &ThickSpace;&ThickSpace; ( c n t [ d ] = a 数 组 中 有 多 少 个 数 字 为 d 的 倍 数 ) \begin{aligned} Ans &amp;= \sum_{i,j,k,l \in[1,N]}{[(a[i],a[j],a[k],a[l]) = 1]}\\ &amp;= \sum_{d=1}^{Max}{\mu(d)\cdot C_{cnt[d]}^{4}}\;\;(cnt[d] = a数组中有多少个数字为d的倍数) \end{aligned} Ans=i,j,k,l[1,N][(a[i],a[j],a[k],a[l])=1]=d=1Maxμ(d)Ccnt[d]4(cnt[d]=ad)

预处理 c n t cnt cnt数组,线性做。

Code:

#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 1e4+100;
bool used[maxn];
vector<int> prime;
int mu[maxn];
int a[maxn];
int cnt[maxn];
void init(){
	mu[1] = 1;
	for (int i=2;i<maxn;i++){
		if (!used[i]){
			prime.push_back(i);
			mu[i] = -1;
		}
		for (int j=0;j<prime.size();j++){
			long long nxt =1ll * i * prime[j];
			if (nxt >= maxn)break;
			used[nxt] = 1;
			if (i % prime[j]){
				mu[nxt] = -mu[i];
			}else{
				mu[nxt] = 0;
				break;
			}
		}
	}
}
long long C(int n,int m){
	if (n <m)return 0ll;
	return 1ll * n * (n-1) * (n-2) * (n-3) / 24;
}
long long work(){
	long long ans = 0;
	for (int i=1;i<=10000;i++){
		ans += 1ll*mu[i] * C(cnt[i],4);
	}
	return ans;
}
int main(){
	init();
	int n;
	while(scanf("%d",&n) != EOF && n){
		memset(cnt,0,sizeof cnt);
		for (int i=0;i<n;i++){
			scanf("%d",a+i);
			for (int j=1;1ll * j * j <=a[i];j++){
				if(a[i] % j == 0){
					cnt[j] ++;
					if(a[i] / j != j){
						cnt[a[i]/j] ++;
					}
				}
			}
		}
		printf("%lld\n",work());
	}
	return 0;
}

CF 1043F

题意

给出一个数组 a a a,从中选择最少的数字,使其 G C D = 1 GCD = 1 GCD=1

注意到在数字范围内,一个数字最多有 7 7 7个不同的素因子。因此答案至多为 7 7 7

[ 1 , 7 ] [1,7] [1,7]内枚举答案,然后求 F ( k ) F(k) F(k)表示选取 k k k个数字且 G C D = 1 GCD = 1 GCD=1的方案数。与上题一样

F ( k ) = ∑ i 1 , i 2... i k ∈ [ 1 , N ] [ ( a [ i 1 ] , a [ i 1 ] . . . a [ i k ] ) = 1 ] = ∑ d = 1 M a x μ ( d ) ⋅ C c n t [ d ] k &ThickSpace;&ThickSpace; ( c n t [ d ] = a 数 组 中 有 多 少 个 数 字 为 d 的 倍 数 ) \begin{aligned} F(k) &amp;= \sum_{i1,i2...ik \in[1,N]}{[(a[i1],a[i1]...a[ik]) = 1]}\\ &amp;= \sum_{d=1}^{Max}{\mu(d)\cdot C_{cnt[d]}^{k}}\;\;(cnt[d] = a数组中有多少个数字为d的倍数) \end{aligned} F(k)=i1,i2...ik[1,N][(a[i1],a[i1]...a[ik])=1]=d=1Maxμ(d)Ccnt[d]k(cnt[d]=ad)
注意到 F ( k ) F(k) F(k)的值会很大,我们只需要判断其是否为0,因此选择大质数取模。
Code:

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <assert.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+100;
const ll MOD = 998244353;
bool used[maxn];
vector<int> prime;
int mu[maxn];
int cnt[maxn];
ll fac[maxn];
void init(){
	mu[1] = 1;
	fac[0]= fac[1] = 1;
	for (int i=2;i<maxn;i++){
		fac[i] = fac[i-1] * i % MOD;
		if (!used[i]){
			prime.push_back(i);
			mu[i] = -1;
		}
		for (int j=0;j<prime.size();j++){
			long long nxt =1ll * i * prime[j];
			if (nxt >= maxn)break;
			used[nxt] = 1;
			if (i % prime[j]){
				mu[nxt] = -mu[i];
			}else{
				mu[nxt] = 0;
				break;
			}
		}
	}
}
long long quick_mod(ll x,ll y){
	ll res = 1;
	while (y){
		if(y&1){
			res = res * x % MOD;
		}
		y >>= 1;
		x = x * x % MOD;
	}
	return res;
}

int n;
int a[maxn];
inline ll C(int n,int m){
	if(n < m)return 0;
	return 1ll * fac[n] * quick_mod(fac[m],MOD-2) % MOD* quick_mod(fac[n-m],MOD-2) % MOD;
}
long long work(int x){
	ll ans = 0;
	for (int i=1;i<=300000;i++){
		(ans += 1ll * mu[i] * C(cnt[i],x))%= MOD;
		(ans += MOD) %= MOD;
	}
	return ans;
}
int main(){
	init();
	scanf("%d",&n);
	for (int i=0;i<n;i++){
		scanf("%d",a+i);
		for (int j=1;1ll* j*j <=a[i];j++){
			if(a[i] %j == 0){
				cnt[j] ++;
				if(a[i]/j != j){
					cnt[a[i]/j] ++;
				}
			}
		}
	}
	int gcd = a[0];
	for (int i=1;i<n;i++){
		gcd = __gcd(gcd,a[i]);
	}
	if(gcd != 1){
		puts("-1");
		return 0;
	}
	for (int i=1;i<=7;i++){
		if(work(i)){
			printf("%d\n",i);
			return 0;
		}
	}
	assert(0);
	return 0;
}

CF 547C

题意

维护一个distinct的数字集合 S S S,每次向其中增加一个数字,或者删除一个数字。在每次操作之后,求出集合中 ( x , y ) = 1 (x,y) = 1 (x,y)=1的点对个数。

只考虑每次操作的数字带来的贡献。即在一个数字集合中,求和一个数字 x x x互质的数字个数,即为 F ( x ) F(x) F(x)

x x x质因数分解,找到 x x x包含的质数,然后直接容斥。

x = p 1 a 1 ⋅ p 2 a 2 . . . ⋅ p k a k x = p1^{a1}\cdot p2^{a2}...\cdot pk^{ak} x=p1a1p2a2...pkak,由数据范围, k ≤ 8 k\leq 8 k8.

F ( x ) = ∑ y ∈ S [ ( x , y ) = 1 ] = ∑ m a s k = 0 1 &lt; &lt; k ( − 1 ) p o p c o u n t ( m a s k ) ⋅ c n t [ P ] &ThickSpace;&ThickSpace; ( P = ∏ i ∈ [ 1 , k ] m a s k &amp; ( 1 &lt; &lt; i ) p i ) \begin{aligned} F(x)&amp;= \sum_{y \in S}{[(x,y) = 1]}\\ &amp;=\sum_{mask = 0}^{1&lt;&lt;k}{(-1)^{popcount(mask)}\cdot cnt[P]} \;\;(P = \prod_{i\in[1,k]}^{mask \&amp; (1&lt;&lt;i)}{pi}) \end{aligned} F(x)=yS[(x,y)=1]=mask=01<<k(1)popcount(mask)cnt[P](P=i[1,k]mask&(1<<i)pi)

直接维护 c n t cnt cnt数组,算每次操作对答案的贡献变化量即可。
Code:

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <assert.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+100;
bool used[maxn];
int a[maxn];
vector<int> prime;
int cnt[maxn];
bool exist[maxn];
void init(){
	for (int i=2;i<maxn;i++){
		if (!used[i]){
			prime.push_back(i);
		}
		for (int j=0;j<prime.size();j++){
			long long nxt =1ll * i * prime[j];
			if (nxt >= maxn)break;
			used[nxt] = 1;
			if (i % prime[j] == 0){
				break;
			}
		}
	}
}
int n,q;
ll work(int x){
	vector<int> my_prime(0);
	int X = x;
	for (int i = 0;i<prime.size();i++){
		int pi = prime[i];
		if(1ll * pi * pi > X)break;
		if (x % pi == 0){
			my_prime.push_back(pi);
			while (x% pi == 0)x /= pi;
		}
	}
	if (x != 1){
		my_prime.push_back(x);
	}
	int sz = my_prime.size();
	ll ans = 0;
	for (int mask = 0;mask < (1<<sz);mask++){
		int cnt1 = __builtin_popcount(mask);
		int sig = 1;
		if(cnt1 &1){
			sig = -1;
		}
		int S = 1;
		for (int i=0;i<sz;i++){
			if (mask & (1<< i)){
				S *= my_prime[i];
			}
		}
		ans += 1ll * sig * cnt[S];
	}
	return ans;
}
inline void add(int ai,int val){
	for (int j=1;1ll* j*j <=ai;j++){
		if(ai %j == 0){
			cnt[j] += val;
			if(ai/j != j){
				cnt[ai/j] += val;
			}
		}
	}
}
ll ans;
int main(){
	init();
	scanf("%d%d",&n,&q);
	for (int i=0;i<n;i++){
		scanf("%d",a+i);
	}
	ans = 0;
	while (q--){
		int idx;
		scanf("%d",&idx);
		int x = a[idx-1];
		if(exist[idx]){
			add(x,-1);
			ans -= work(x);
			exist[idx] = 0;
		}else{
			ans += work(x);
			add(x,1);
			exist[idx] = 1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

洛谷 P3312

题意:

多组询问,每次询问有一个 N ∗ M N*M NM的二维数组 A A A,其中 A [ x ] [ y ] = ∑ d ∣ ( x , y ) d A[x][y] = \sum_{d | (x,y)}d A[x][y]=d(x,y)d,求这个数组中值小于等于 a a a的所有值之和。

f ( d ) f(d) f(d)表示d的所有因子之和,即
f ( d ) = ∑ x ∣ d x \begin{aligned} f(d) = \sum_{x|d}x \end{aligned} f(d)=xdx
g ( d ) g(d) g(d)表示 x ∈ [ 1 , N ] x\in[1,N] x[1,N], y ∈ [ 1 , M ] y\in[1,M] y[1,M],且 ( x , y ) = d (x,y) = d (x,y)=d的点对个数。即
g ( d ) = ∑ i = 1 n ∑ j = 1 m [ ( i , j ) = d ] = ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ ( i , j ) = 1 ] = ∑ x = 1 min ⁡ ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( x ) ⋅ ⌊ n x d ⌋ ⋅ ⌊ m x d ⌋ \begin{aligned} g(d) &amp;= \sum_{i=1}^{n}\sum_{j=1}^{m}[(i,j) = d]\\ &amp;= \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[(i,j) = 1]\\ &amp;= \sum_{x=1}^{\min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(x)\cdot \lfloor\frac{n}{xd}\rfloor\cdot \lfloor\frac{m}{xd}\rfloor \end{aligned} g(d)=i=1nj=1m[(i,j)=d]=i=1dnj=1dm[(i,j)=1]=x=1min(dn,dm)μ(x)xdnxdm

因此设 F ( a ) F(a) F(a)表示对于给定的 a a a的答案。即
F ( a ) = ∑ d = 1 min ⁡ ( n , m ) f ( d ) ⋅ g ( d ) ⋅ [ f ( d ) &lt; = a ] = ∑ d = 1 min ⁡ ( n , m ) f ( d ) ⋅ [ f ( d ) &lt; = a ] ⋅ ∑ k = 1 min ⁡ ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) ⋅ ⌊ n k d ⌋ ⋅ ⌊ m k d ⌋ = ∑ x = 1 min ⁡ ( n , m ) ⌊ n x ⌋ ⋅ ⌊ m x ⌋ ⋅ ∑ d ∣ x f ( d ) ⋅ μ ( x / d ) ⋅ [ f ( d ) &lt; = a ] \begin{aligned} F(a) &amp;= \sum_{d=1}^{\min(n,m)}f(d)\cdot g(d) \cdot [f(d) &lt;=a]\\ &amp;=\sum_{d=1}^{\min(n,m)}f(d)\cdot [f(d)&lt;=a]\cdot\sum_{k=1}^{\min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(k)\cdot \lfloor\frac{n}{kd}\rfloor \cdot \lfloor\frac{m}{kd}\rfloor\\ &amp;= \sum_{x=1}^{\min(n,m)}\lfloor\frac{n}{x}\rfloor\cdot\lfloor\frac{m}{x}\rfloor\cdot\sum_{d | x}f(d)\cdot\mu(x/d)\cdot[f(d) &lt;= a] \end{aligned} F(a)=d=1min(n,m)f(d)g(d)[f(d)<=a]=d=1min(n,m)f(d)[f(d)<=a]k=1min(dn,dm)μ(k)kdnkdm=x=1min(n,m)xnxmdxf(d)μ(x/d)[f(d)<=a]
h ( x ) = ∑ d ∣ x f ( d ) ⋅ μ ( x / d ) ⋅ [ f ( d ) &lt; = a ] h(x) = \sum_{d | x}f(d)\cdot\mu(x/d)\cdot[f(d) &lt;= a] h(x)=dxf(d)μ(x/d)[f(d)<=a]
因此离线所有询问,然后按照预处理 μ \mu μ f f f,并且按照 f f f数组从小到大的顺序,去更新 h h h数组的值,并且回答询问,使用 B I T BIT BIT维护 h h h数组, N ⋅ l o g n \sqrt{N}\cdot logn N logn回答询问。

Code:

// Created by calabash_boy on 18-10-18.
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "[%s %3d]: " fmt "\n", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0);
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 1e5+100;
typedef unsigned int uint;
/************* header ******************/
bool used[maxn];
int mu[maxn];
vector<int> prime;
struct BIT{
    ll sum[maxn];
    int lowbit(int x){
        return x & (-x);
    }
    void add(int x,ll val){
        while (x<maxn){
            sum[x] += val;
            x += lowbit(x);
        }
    }
    ll query(int x){
        ll res = 0;
        while (x){
            res += sum[x];
            x -= lowbit(x);
        }
        return res;
    }
    ll query(int l,int r){
        return query(r) - query(l-1);
    }
}bit;
ll f[maxn];
int rk[maxn];
void seive(){
    mu[1] = 1;
    for (int i=2;i<maxn;i++){
        if (!used[i]){
            prime.push_back(i);
            mu[i] = -1;
        }
        for (int j = 0;j < prime.size();j++){
            ll nxt = 1ll * i * prime[j];
            if (nxt >= maxn)break;
            used[nxt] = 1;
            if (i % prime[j]){
                mu[nxt] = -mu[i];
            }else{
                mu[nxt] = 0;
                break;
            }
        }
    }
    for (int i=1;i<maxn;i++){
        for (int j = i;j < maxn;j += i){
            f[j] += i;
        }
        rk[i] = i;
    }
    sort(rk+1,rk+maxn,[](int x,int y){
        return f[x] < f[y];
    });
}

typedef tuple<int,int,int,int> Query;
vector<tuple<int,int,int,int> > Q;
int ans[maxn];
ll work(int n,int m){
    ll res = 0;
    int top = min(n,m);
    for (int i=1,j;i<=top;i=j+1){
        j = min(n/(n/i),m/(m/i));
        res += 1ll * (n/i) * (m/i) * bit.query(i,j);
    }
    return res & ((1ll<<31)-1);
}
int main(){
    seive();
    int T;
    scanf("%d",&T);
    for (int i=1;i<=T;i++){
        int n,m,a;
        scanf("%d%d%d",&n,&m,&a);
        Q.push_back(make_tuple(n,m,a,i));
    }
    sort(Q.begin(),Q.end(),[](Query q1,Query q2){
        return get<2>(q1) < get<2>(q2);
    });

    for (int i=0,j=1;i<Q.size();i++){
        int n,m,a,id;
        tie(n,m,a,id) = Q[i];
        while (j<maxn && f[rk[j]] <= a){
            int d = rk[j];
            for (int idx = d;idx < maxn;idx += d){
                bit.add(idx,f[d] * mu[idx/d]);
            }
            j++;
        }
        ans[id] = work(n,m);
    }
    for (int i=1;i<=T;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

BZOJ 2154

题意 : 求 ∑ i = 1 n ∑ j = 1 m [ i , j ] \sum_{i=1}^{n}\sum_{j=1}^{m}[i,j] i=1nj=1m[i,j]

A n s = ∑ i = 1 n ∑ j = 1 m [ i , j ] = ∑ i = 1 n ∑ j = 1 m i ⋅ j ( i , j ) = ∑ d = 1 min ⁡ ( n , m ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i ⋅ d ∗ j ⋅ d d ⋅ [ ( i , j ) = 1 ] = ∑ d = 1 min ⁡ ( n , m ) d ⋅ ∑ k = 1 min ⁡ ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) ⋅ ∑ i = 1 ⌊ n k d ⌋ i k ⋅ ∑ j = 1 ⌊ m k d ⌋ j k &ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; ( 设 F ( x ) = x ⋅ ( x + 1 ) 2 ) = ∑ d = 1 min ⁡ ( n , m ) d ⋅ ∑ k = 1 min ⁡ ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) ⋅ k 2 ⋅ F ( ⌊ n k d ⌋ ) ⋅ F ( ⌊ m k d ⌋ ) &ThickSpace;&ThickSpace;&ThickSpace; ( 令 x = k d ) = ∑ x = 1 min ⁡ ( n , m ) F ( ⌊ n x ⌋ ) ⋅ F ( ⌊ m x ⌋ ) ⋅ ∑ k ∣ x μ ( k ) ⋅ k 2 ⋅ x k &ThickSpace;&ThickSpace;&ThickSpace; ( 令 g ( x ) = x ⋅ ∑ k ∣ x μ ( k ) ⋅ k ) = ∑ x = 1 min ⁡ ( n , m ) F ( ⌊ n x ⌋ ) ⋅ F ( ⌊ m x ⌋ ) ⋅ g ( x ) \begin{aligned} Ans &amp;=\sum_{i=1}^{n}\sum_{j=1}^{m}[i,j]\\ &amp;=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{i\cdot j}{(i,j)}\\ &amp;=\sum_{d=1}^{\min(n,m)}\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\frac{i\cdot d * j\cdot d}{d}\cdot[(i,j) = 1]\\ &amp;=\sum_{d=1}^{\min(n,m)}d\cdot\sum_{k=1}^{\min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(k)\cdot\sum_{i=1}^{\lfloor\frac{n}{kd}\rfloor}ik\cdot\sum_{j=1}^{\lfloor\frac{m}{kd}\rfloor}jk\;\;\;\;(设F(x) = \frac{x\cdot(x+1)}{2})\\ &amp;=\sum_{d=1}^{\min(n,m)}d\cdot\sum_{k=1}^{\min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(k)\cdot k^2\cdot F(\lfloor\frac{n}{kd}\rfloor)\cdot F(\lfloor\frac{m}{kd}\rfloor)\;\;\;(令x = kd)\\ &amp;=\sum_{x=1}^{\min(n,m)}F(\lfloor\frac{n}{x}\rfloor)\cdot F(\lfloor\frac{m}{x}\rfloor)\cdot \sum_{k | x}\mu(k)\cdot k^2 \cdot \frac{x}{k}\;\;\;(令g(x) = x\cdot\sum_{k|x}\mu(k)\cdot k)\\ &amp;=\sum_{x=1}^{\min(n,m)}F(\lfloor\frac{n}{x}\rfloor)\cdot F(\lfloor\frac{m}{x}\rfloor)\cdot g(x) \end{aligned} Ans=i=1nj=1m[i,j]=i=1nj=1m(i,j)ij=d=1min(n,m)i=1dnj=1dmdidjd[(i,j)=1]=d=1min(n,m)dk=1min(dn,dm)μ(k)i=1kdnikj=1kdmjk(F(x)=2x(x+1))=d=1min(n,m)dk=1min(dn,dm)μ(k)k2F(kdn)F(kdm)(x=kd)=x=1min(n,m)F(xn)F(xm)kxμ(k)k2kx(g(x)=xkxμ(k)k)=x=1min(n,m)F(xn)F(xm)g(x)
观察到 μ \mu μ x x x都是积性的,因此 μ ( x ) ⋅ x \mu(x)\cdot x μ(x)x是积性的,然后一个设 x = a ⋅ b , ( a , b ) = 1 x = a\cdot b,(a,b) = 1 x=ab,(a,b)=1,则 ∀ k ∣ x \forall k | x kx,必然有唯一的分解即,存在唯一的一对 k 1 , k 2 k1,k2 k1,k2,使得 k = k 1 ⋅ k 2 k = k1 \cdot k2 k=k1k2 k 1 ∣ a k1 | a k1a, k 2 ∣ b k2 | b k2b,因此可以得知 g ( x ) g(x) g(x)是积性的(或者直接由欧拉函数同理),因此直接筛出 g g g,然后根号求答案。

Code:

// Created by calabash_boy on 18-10-18.
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
# define _debug(fmt, ...) fprintf(stderr, "[%s %3d]: " fmt "\n", \
  __func__,__LINE__, ##__VA_ARGS__)
#else
# define _debug(...) (void(0))
#endif
#define PB(x) push_back(x)
#define rep(i,l,r) for (int i = l,_ = r;i< _;i++)
#define REP(i,l,r) for (int i=l,_=r;i<=_;i++)
#define leave(x) do {cout<<#x<<endl;fflush(stdout);return 0;}while (0);
#define untie do{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);}while (0)
#define range(x) x.begin(),x.end()
typedef long long LL;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef long double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int inf = 0x3f3f3f3f;
const ll inf_ll = 0x3f3f3f3f3f3f3f3fLL;
const ll mod = 20101009;
const int maxn = 1e7+100;
typedef unsigned int uint;
/************* header ******************/
bool used[maxn];
int mu[maxn];
vector<int> prime;
ll f[maxn];
int low[maxn];
void seive(int maxn){
    mu[1] = 1;
    f[1] = 1;
    for (int i=2;i<=maxn;i++){
        if (!used[i]){
            prime.push_back(i);
            mu[i] = -1;
            f[i] = 1ll * i * (1-i) % mod;
            low[i] = i;
        }
        for (int j = 0;j < prime.size();j++){
            ll nxt = 1ll * i * prime[j];
            if (nxt > maxn)break;
            used[nxt] = 1;
            if (i % prime[j]){
                low[nxt] = prime[j];
                mu[nxt] = -mu[i];
                f[nxt] = f[i] * f[prime[j]] % mod;
            }else{
                low[nxt] = prime[j] * low[i];
                mu[nxt] = 0;
                if (low[nxt] != nxt){
                    f[nxt] = 1ll * f[low[nxt]] * f[nxt/low[nxt]] % mod;
                }else{
                    f[nxt] = 1ll * nxt * (1-prime[j]) % mod;
                }
                break;
            }
        }
    }
    for (int i=1;i<=maxn;i++){
        (f[i] += f[i-1] + mod)%= mod;
    }
}
inline ll F(ll n,ll x){
    ll top = n/x;
    return 1ll * (top) * (top + 1)/2 % mod;
}
ll work(ll m,ll n){
    ll ans = 0;
    int top = min(n,m);
    for (int i=1,j;i<=top;i=j+1){
        j = min(n/(n/i),m/(m/i));
        (ans += 1ll * F(n,i) * F(m,i)%mod * (f[j] - f[i-1]) % mod + mod) %= mod;
    }
    return (ans+mod)%mod;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    seive(max(n,m));
    printf("%lld\n",work(n,m));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值