洛谷 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)=pisprime∑i=1∑Nj=1∑M[(i,j)=p]=pisprime∑i=1∑⌊pN⌋j=1∑⌊pM⌋[(i,j)=1]=pisprime∑d=1∑min(⌊pN⌋,⌊pM⌋)μ(d)⋅⌊pdN⌋⋅⌊pdM⌋(令k=pd)=k=1∑min(N,M)⌊kN⌋⋅⌊kM⌋⋅d∣k∑μ(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(a−1,d)−F(b,c−1)+F(a−1,c−1)
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) &= \sum_{i=1}^{N}{\sum_{j=1}^{M}{[(x,y) = k]}}\\ &= \sum_{i=1}^{\lfloor\frac{N}{k}\rfloor}{\sum_{j=1}^{\lfloor\frac{M}{k}\rfloor}{[(x,y) = 1]}}\\ &= \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=1∑Nj=1∑M[(x,y)=k]=i=1∑⌊kN⌋j=1∑⌊kM⌋[(x,y)=1]=d=1∑min(⌊kN⌋,⌊kM⌋)μ(d)⋅⌊kdN⌋⋅⌊kdM⌋
预处理
μ
\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      ( c n t [ d ] = a 数 组 中 有 多 少 个 数 字 为 d 的 倍 数 ) \begin{aligned} Ans &= \sum_{i,j,k,l \in[1,N]}{[(a[i],a[j],a[k],a[l]) = 1]}\\ &= \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=1∑Maxμ(d)⋅Ccnt[d]4(cnt[d]=a数组中有多少个数字为d的倍数)
预处理 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
    
(
c
n
t
[
d
]
=
a
数
组
中
有
多
少
个
数
字
为
d
的
倍
数
)
\begin{aligned} F(k) &= \sum_{i1,i2...ik \in[1,N]}{[(a[i1],a[i1]...a[ik]) = 1]}\\ &= \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=1∑Maxμ(d)⋅Ccnt[d]k(cnt[d]=a数组中有多少个数字为d的倍数)
注意到
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=p1a1⋅p2a2...⋅pkak,由数据范围, k ≤ 8 k\leq 8 k≤8.
F ( x ) = ∑ y ∈ S [ ( x , y ) = 1 ] = ∑ m a s k = 0 1 < < k ( − 1 ) p o p c o u n t ( m a s k ) ⋅ c n t [ P ]      ( P = ∏ i ∈ [ 1 , k ] m a s k & ( 1 < < i ) p i ) \begin{aligned} F(x)&= \sum_{y \in S}{[(x,y) = 1]}\\ &=\sum_{mask = 0}^{1<<k}{(-1)^{popcount(mask)}\cdot cnt[P]} \;\;(P = \prod_{i\in[1,k]}^{mask \& (1<<i)}{pi}) \end{aligned} F(x)=y∈S∑[(x,y)=1]=mask=0∑1<<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 N∗M的二维数组 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)=x∣d∑x
设
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) &= \sum_{i=1}^{n}\sum_{j=1}^{m}[(i,j) = d]\\ &= \sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[(i,j) = 1]\\ &= \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=1∑nj=1∑m[(i,j)=d]=i=1∑⌊dn⌋j=1∑⌊dm⌋[(i,j)=1]=x=1∑min(⌊dn⌋,⌊dm⌋)μ(x)⋅⌊xdn⌋⋅⌊xdm⌋
因此设
F
(
a
)
F(a)
F(a)表示对于给定的
a
a
a的答案。即
F
(
a
)
=
∑
d
=
1
min
(
n
,
m
)
f
(
d
)
⋅
g
(
d
)
⋅
[
f
(
d
)
<
=
a
]
=
∑
d
=
1
min
(
n
,
m
)
f
(
d
)
⋅
[
f
(
d
)
<
=
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
)
<
=
a
]
\begin{aligned} F(a) &= \sum_{d=1}^{\min(n,m)}f(d)\cdot g(d) \cdot [f(d) <=a]\\ &=\sum_{d=1}^{\min(n,m)}f(d)\cdot [f(d)<=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\\ &= \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) <= a] \end{aligned}
F(a)=d=1∑min(n,m)f(d)⋅g(d)⋅[f(d)<=a]=d=1∑min(n,m)f(d)⋅[f(d)<=a]⋅k=1∑min(⌊dn⌋,⌊dm⌋)μ(k)⋅⌊kdn⌋⋅⌊kdm⌋=x=1∑min(n,m)⌊xn⌋⋅⌊xm⌋⋅d∣x∑f(d)⋅μ(x/d)⋅[f(d)<=a]
设
h
(
x
)
=
∑
d
∣
x
f
(
d
)
⋅
μ
(
x
/
d
)
⋅
[
f
(
d
)
<
=
a
]
h(x) = \sum_{d | x}f(d)\cdot\mu(x/d)\cdot[f(d) <= a]
h(x)=∑d∣xf(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=1n∑j=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
        
(
设
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
⌋
)
      
(
令
x
=
k
d
)
=
∑
x
=
1
min
(
n
,
m
)
F
(
⌊
n
x
⌋
)
⋅
F
(
⌊
m
x
⌋
)
⋅
∑
k
∣
x
μ
(
k
)
⋅
k
2
⋅
x
k
      
(
令
g
(
x
)
=
x
⋅
∑
k
∣
x
μ
(
k
)
⋅
k
)
=
∑
x
=
1
min
(
n
,
m
)
F
(
⌊
n
x
⌋
)
⋅
F
(
⌊
m
x
⌋
)
⋅
g
(
x
)
\begin{aligned} Ans &=\sum_{i=1}^{n}\sum_{j=1}^{m}[i,j]\\ &=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{i\cdot j}{(i,j)}\\ &=\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]\\ &=\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})\\ &=\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)\\ &=\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)\\ &=\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=1∑nj=1∑m[i,j]=i=1∑nj=1∑m(i,j)i⋅j=d=1∑min(n,m)i=1∑⌊dn⌋j=1∑⌊dm⌋di⋅d∗j⋅d⋅[(i,j)=1]=d=1∑min(n,m)d⋅k=1∑min(⌊dn⌋,⌊dm⌋)μ(k)⋅i=1∑⌊kdn⌋ik⋅j=1∑⌊kdm⌋jk(设F(x)=2x⋅(x+1))=d=1∑min(n,m)d⋅k=1∑min(⌊dn⌋,⌊dm⌋)μ(k)⋅k2⋅F(⌊kdn⌋)⋅F(⌊kdm⌋)(令x=kd)=x=1∑min(n,m)F(⌊xn⌋)⋅F(⌊xm⌋)⋅k∣x∑μ(k)⋅k2⋅kx(令g(x)=x⋅k∣x∑μ(k)⋅k)=x=1∑min(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=a⋅b,(a,b)=1,则
∀
k
∣
x
\forall k | x
∀k∣x,必然有唯一的分解即,存在唯一的一对
k
1
,
k
2
k1,k2
k1,k2,使得
k
=
k
1
⋅
k
2
k = k1 \cdot k2
k=k1⋅k2且
k
1
∣
a
k1 | a
k1∣a,
k
2
∣
b
k2 | b
k2∣b,因此可以得知
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;
}