莫比乌斯
\(\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;=\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}\)
数论分块 + 前缀和
#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;
}
闲话:
为什么大家做题是越做越熟练,我是越做越茫然?
\(\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;
}
(我裂开来)
然后我们观察式子
我们就可以设
这个式子是不是可以预处理一下?
就是三个前缀和拼凑!
然后数论分块可以求出\(behind(x,y)\)的值!
而
又是一个数论分块!
所以就解决了
#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;
}
我们假设
则
这个思路有点难走,那我们换个思路
我们设前面的式子为sum(n,m),则
这个可以用数论分块
然后我们发现其实后面的式子和上面没有用减法原理的式子一样,所以失败
这个时候,我们发现我们最开始枚举的东西就是除数函数!
(其实我没有发现,是看题解才知道的)
所以我们可以写出sum(n,m)的另一个版本
此时,前面的求和可以分块\(O(n + m)\),后面的是个积性函数,可以预处理,然后就是O(1)
但是,不是每个东西都对答案有贡献,所以我们要排除掉没有贡献的.
然后就需要维护答案,想到维护这个词,在看一下式子,
这不就是树状数组吗?
所以我鸽了……