bzoj3529 [SDOI2014]数表
solution
“能同时整除i和j的数”其实就是\(gcd(i,j)\)的因数。
所以题目就是要求
\[\sum\limits_{g=1,\sigma(g)\le a}\sigma(g)\sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{g}\rfloor}[gcd(i,j)=1]
\]
\[=\sum\limits_{g=1,\sigma(g)\le a}\sigma(g)\sum\limits_{d=1}^{\lfloor\frac{n}{g}\rfloor}\mu(d)\lfloor\frac{n}{dg}\rfloor\lfloor\frac{m}{dg}\rfloor
\]
令\(T=gd\)
\[=\sum\limits_{T=1}^n\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum\limits_{g|T,\sigma(g)\le a}\sigma(g)\mu(\frac{T}{g})
\]
设\(f(i)=\sum\limits_{d|i}\sigma(d)\mu(\frac{i}{d})\)
后面这部分只有当\(\sigma(g)\le a\)时才会产生贡献,如果没有\(\sigma(g)\le a\)这个限制的话,我们可以直接先预处理\(f\)(枚举\(g\),再枚举\(g\)的倍数即可),然后前缀和数论分块,搞定。
然后考虑如何处理这个限制,我们可以把询问离线下来,然后按照\(a\)排序,将\(\sigma(g)\)也按照大小排序,从小到大的处理询问,当当前的\(g\)可以产生贡献时,就更新\(f(kg)\)。
然后还要维护前缀和,所以再用个树状数组维护即可。
复杂度\(O(nlog^2n+Q\sqrt{n}logn)\)
code
/*
* @Author: wxyww
* @Date: 2020-04-23 10:59:26
* @Last Modified time: 2020-04-23 11:39:46
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
typedef unsigned int Uint;
const int N = 100010;
ll read() {
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1; c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0'; c = getchar();
}
return x * f;
}
ll o[N];
int ans[N],vis[N],prime[N];
Uint mu[N];
struct node {
int n,m,x,id;
}a[N];
#define pi pair<ll,int>
pi t[N];
bool cmp(const node &A,const node &B) {
return A.x < B.x;
}
void pre() {
for(int i = 1;i <= 100000;++i)
for(int j = i;j <= 100000;j += i)
o[j] += i;
for(int i = 1;i <= 100000;++i)
t[i] = make_pair(o[i],i);
mu[1] = 1;
int tot = 0;
for(int i = 2;i <= 100000;++i) {
if(!vis[i]) {
prime[++tot] = i;
mu[i] = -1;
}
for(int j = 1;j <= tot && prime[j] * i <= 100000;++j) {
vis[prime[j] * i] = 1;
if(i % prime[j]) mu[i * prime[j]] = -mu[i];
else break;
}
}
}
Uint tree[N];
void update(int pos,Uint c) {
while(pos <= 100000) {
tree[pos] += c;
pos += pos & -pos;
}
}
Uint query(int pos) {
Uint ret = 0;
while(pos) {
ret += tree[pos];
pos -= pos & -pos;
}
return ret;
}
int main() {
int Q = read();
for(int i = 1;i <= Q;++i) {
a[i].n = read(),a[i].m = read();a[i].x = read();
a[i].id = i;
}
pre();
sort(a + 1,a + Q + 1,cmp);
sort(t + 1,t + 100001);
int p = 1;
for(int i = 1;i <= Q;++i) {
while(p <= 100000 && t[p].first <= a[i].x) {
for(int j = t[p].second;j <= 100000;j += t[p].second) {
update(j,(Uint)t[p].first * mu[j / t[p].second]);
}
++p;
}
int n = a[i].n,m = a[i].m;
if(n > m) swap(n,m);
Uint ret = 0;
for(int l = 1,r;l <= n;l = r + 1) {
r = min(n / (n / l),m / (m / l));
ret += (Uint) (n / l) * (m / l) * (query(r) - query(l - 1));
}
ans[a[i].id] = ret;
}
for(int i = 1;i <= Q;++i) cout<<(ans[i] & ((1ll << 31) - 1))<<endl;
return 0;
}
===================================================================================
该怎麼去形容为思念酝酿的痛
夜空霓虹都是我不要的繁荣 ===================================================================================