题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3529
思路:这题的思路还是很好的。。。实际上是披上了数论外衣的数据结构狼。。。
首先我们化简这个式子,得到:
∑i=1n∑j=1mf(gcd(i,j))[f(gcd(i,j))<=a]
=∑i=1min(n,m)⌊ni⌋⌊mi⌋∑d|if(d)μ(id)[f(d)<=a]
按照一般的思路,因为 a 的限制,我们离线按
根据调和级数我们总的复杂度是: O(nlog2n+Qn√logn) ,当然,如果强制在线我们将树状数组换成主席树就可以了,复杂度不变。
作为一个正常人我们是不会满足于这么不优美的复杂度的。。。下面我来口胡一番离线复杂度更科学的做法:
通过前面我们可以体会的这根本就是一道数据结构题嘛。。。我们要完成的操作无非是区间和查询,单点修改
考虑分块,我们维护后面那一个东西,设两个参数 A,B 我们还是 a 从小到大枚举,当
当 d>A 时,我们定期重构,暴力修改,维护一个计数器 cnt 表示当前修改了的不同位置有 cnt 个,将这些位置及其贡献存下来,当 cnt>B 时将贡献暴力添加入数组扫描一遍,查询的时候分为两个部分:一个是原数组,另一个是你存下来位置的贡献,设 f[x]=∑ni=x⌊ni⌋
复杂度是: O(A∗n+Q(B+n√)+f[n−A]∗nB)
首先我们注意到 f[n]<=nlogn ,根据均值不等式(或者打表)我们能取到一个最优值,很明显这个复杂度是优于上面那个做法的。。。但常数不算太小不知道写起来能不能跑过。。。
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define lowbit(x)(x&(-x))
#define N 100002
#define M 20002
using namespace std;
struct node { int n,m,a,ID;};
node q[M];
struct data { int val,ID; };
data f[N];
int cnt,mu[N],Rou[N],ans[M],prime[N],c[N],Q;
bool not_prime[N];
bool cmp(node x,node y){ return x.a < y.a;};
bool cmp1(data x,data y){ return x.val < y.val;};
void in(int &x){
char c;
while (!isdigit(c = getchar()));
x = (c ^ 48);
while (isdigit(c = getchar())) x = 10 * x + (c ^ 48);
}
void S_(){
memset(not_prime,0,sizeof(not_prime));
cnt = 0; mu[1] = 1;
for (int i = 2;i < N - 1; ++i){
if (!not_prime[i]) prime[++cnt] = i,mu[i] = -1;
for (int j = 1;j <= cnt; ++j)
if (prime[j] * i >= N - 1) break;
else {
not_prime[prime[j] * i] = 1;
if (i % prime[j]) mu[i * prime[j]] = -mu[i];
else { mu[i * prime[j]] = 0; break; }
}
}
memset(Rou,0,sizeof(Rou));
for (int i = 1;i < N - 1; ++i)
for (int j = i;j < N - 1; j += i)
Rou[j] += i;
for (int i = 1;i < N - 1; ++i)
f[i].val = Rou[i],f[i].ID = i;
sort(f + 1,f + N - 2 + 1,cmp1);
}
void add(int pos,int v){
for (int x = pos;x < N - 1; x += lowbit(x)) c[x] += v;
}
int query(int pos){
int sum = 0;
for (int x = pos;x;x -= lowbit(x)) sum += c[x];
return sum;
}
void add_it(int x){
for (int j = x,t = 1;j < N - 1; j += x,++t)
add(j,Rou[x] * mu[t]);
}
int main(){
S_();
in(Q);
//scanf("%d",&Q);
for (int i = 1;i <= Q; ++i){
//scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a);
in(q[i].n); in(q[i].m); in(q[i].a);
q[i].ID = i;
if (q[i].n > q[i].m) swap(q[i].n,q[i].m); }
sort(q + 1,q + Q + 1,cmp);
int j = 1;
memset(c,0,sizeof(c));
for (int i = 1;i <= Q; ++i){
while (j < N - 1&&f[j].val <= q[i].a) add_it(f[j++].ID);
int ans_ = 0,n = q[i].n,m = q[i].m;
for (int o1 = 1,o2;o1 <= n; o1 = o2 + 1){
o2 = min(n / (n / o1),m / (m / o1));
ans_ += (n / o1) * (m / o1) * (query(o2) - query(o1 - 1));
}
ans[q[i].ID] = ans_;
}
for (int i = 1;i <= Q; ++i) printf("%d\n",ans[i]&((1 << 31) - 1));
return 0;
}
总结: