3529: [Sdoi2014]数表
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1186 Solved: 604
[ Submit][ Status][ Discuss]
Description
有一张N×m的数表,其第i行第j列(1 < =i < =礼,1 < =j < =m)的数值为
能同时整除i和j的所有自然数之和。给定a,计算数表中不大于a的数之和。
Input
输入包含多组数据。
输入的第一行一个整数Q表示测试点内的数据组数,接下来Q行,每行三个整数n,m,a(|a| < =10^9)描述一组数据。
Output
对每组数据,输出一行一个整数,表示答案模2^31的值。
Sample Input
4 4 3
10 10 5
Sample Output
148
HINT
1 < =N.m < =10^5 , 1 < =Q < =2×10^4
Source
莫比乌斯反演,,默认n <= m
令F[i] = i的所有约数和
题目要求的即是∑F[i] (F[i] <= a)
先忽略a的限制
换个思路,令f[i] = 以i为最大公约数的对数
ans = ∑f[i]*F[I] = ∑F[i]*∑u(d/i)*[n/d]*[m/d] (i|d)
转换一下
ans = ∑[n/d]*[m/d]∑F[i]*u(d/i) 只要想办法处理好∑F[i]*u(d/i) 的前缀和,每次询问就能在根号n内完成
现在有了a的限制,,
先读入所有询问,按照a排序,我们需要的只有F[i] <= a的F
前缀和什么的,树状数组就行
每次要新增F的时候,根据公式,只需要维护i的每个倍数就行
至于F,用筛法O(nlogn),毕竟∑(n/i) = nlogn
嗯,,对∑的运算不够熟练,这个公式拆了好久,,GG
然后,第一次写的时候居然一边做一边取模,强行T一发
(明明把所有数字的约数合起来都不会爆long long啊)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
typedef long long LL;
const int maxn = 1E5 + 10;
const LL mo = (1LL<<31LL);
struct data{
LL F; int pos;
bool operator < (const data &b) const {
return F < b.F;
}
}f[maxn];
struct Q{
int n,m,a,num;
Q(int _n = 0,int _m = 0,int _a = 0,int _num = 0) {
n = _n; m = _m; a = _a; num = _num;
}
bool operator < (const Q &b) const {
return a < b.a;
}
}query[maxn];
int T,ans[maxn];
LL c[maxn],mu[maxn];
bool not_prime[maxn];
void Add(LL F,int pos)
{
for (int i = 1; i*pos < maxn; i++)
for (int j = i*pos; j < maxn; j += j&-j)
c[j] += F*mu[i];
}
LL sum(int pos)
{
LL ret = 0;
for (; pos > 0; pos -= pos&-pos)
ret += c[pos];
return ret;
}
int Work(LL n,LL m)
{
int last,tail = min(n,m);
LL ret = 0;
for (LL i = 1; i <= tail; i = last + 1) {
last = min(n/(n/i),m/(m/i));
ret += (n/i)*(m/i)*(sum(last) - sum(i-1));
}
return ret%mo;
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
for (int i = 1; i < maxn; i++) mu[i] = 1;
for (int i = 2; i < maxn; i++)
if (!not_prime[i]) {
mu[i] = -1;
for (int j = 2; j*i < maxn; j++) {
mu[j*i] *= mu[i];
if (j % i == 0) mu[j*i] = 0;
not_prime[j*i] = 1;
}
}
for (int i = 1; i < maxn; i++) {
f[i].pos = i;
for (int j = 1; j*i < maxn; j++) f[j*i].F += 1LL*i;
}
sort(f + 1,f + maxn);
cin >> T;
for (int i = 1; i <= T; i++) {
int x,y,z; scanf("%d%d%d",&x,&y,&z);
query[i] = Q(x,y,z,i);
}
sort(query + 1,query + T + 1);
int tail = 1;
for (int i = 1; i <= T; i++) {
while (tail < maxn && f[tail].F <= query[i].a) Add(f[tail].F,f[tail].pos),++tail;
ans[query[i].num] = Work(query[i].n,query[i].m);
}
for (int i = 1; i <= T; i++) printf("%d\n",ans[i]);
return 0;
}