3529: [Sdoi2014]数表
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 2049 Solved: 1027
[ 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
2
4 4 3
10 10 5
4 4 3
10 10 5
Sample Output
20
148
148
HINT
1 < =N.m < =10^5 , 1 < =Q < =2×10^4
Source
解题思路:这题是莫比乌斯反演里面比较难得一个,首先要反演半天,得到一个公式,然后还要离线处理,最后还要用一个树状数组维护一下,中间的莫比乌斯函数和约数和函数都是积性函数,所以都可以线性筛。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 100000 + 10;
const unsigned int mod = (1<<31);
struct query
{
int N, M;
int id;
int A;
bool operator <(const query &res) const
{
return A < res.A;
}
} Query[maxn];
struct node
{
int id;
LL value;
} Node[maxn];
LL d[maxn];//约数和
LL Tree[maxn];
LL ans[maxn];
int mu[maxn];
bool valid[maxn];
int prime[maxn];
void Mobius()
{
int tot = 0;
memset(valid, true, sizeof(valid));
mu[1] = 1;
d[1] = 1;
for(int i = 2; i <= 100000; i++)
{
if(valid[i])
{
prime[++tot] = i;
d[i] = (LL)(i + 1);
mu[i] = -1;
}
for(int j = 1; j <= tot && i * prime[j] <= 100000; j++)
{
valid[i * prime[j]] = false;
if(i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
//d[i * prime[j]] = d[i] + (d[i] - d[i / prime[j]]) * prime[j];
int temp = i;
while(temp % prime[j] == 0)
{
temp /= prime[j];
}
d[i * prime[j]] = d[temp] + d[i] * prime[j];
break;
}
mu[i * prime[j]] = -mu[i];
d[i * prime[j]] = d[i] * (prime[j] + 1);
}
}
}
int lowbit(int x)
{
return x&(-x);
}
void add(int loc, LL value)
{
for(int i = loc; i <= 100000; i += lowbit(i))
{
Tree[i] += value;
Tree[i] %= mod;
}
}
LL get(int loc)
{
LL sum = 0;
for(int i = loc; i >= 1; i -= lowbit(i))
{
sum += Tree[i];
sum %= mod;
}
return sum;
}
void init()
{
memset(Tree, 0, sizeof(Tree));
memset(ans, 0, sizeof(ans));
}
bool cmp(node n1, node n2)
{
return n1.value < n2.value;
}
int Q;
int main()
{
//freopen("C:\\Users\\creator\\Desktop\\in1.txt","r",stdin) ;
//freopen("C:\\Users\\creator\\Desktop\\out.txt","w",stdout) ;
Mobius();
for(int i = 1; i <= 100000; i++)
{
Node[i].id = i;
Node[i].value = d[i];
}
sort(Node + 1, Node + 100001, cmp);
scanf("%d", &Q);
init();
for(int i = 1; i <= Q; i++)
{
scanf("%d%d%d", &Query[i].N, &Query[i].M, &Query[i].A);
Query[i].id = i;
}
sort(Query + 1, Query + Q + 1);
int judge = 1;
int L = 1;
while(judge <= Q)
{
while(Node[L].value <= Query[judge].A && L <= 100000)
{
int id = Node[L].id;
LL value = Node[L].value;
for(int j = id; j <= 100000; j += id)
{
add(j, value * mu[j / id]);
}
L++;
}
int N = Query[judge].N;
int M = Query[judge].M;
LL term = 0;
int Min = min(N, M);
int last;
for(int j = 1; j <= Min; j = last + 1)
{
last = min(min(N / (N / j), M / (M / j)), Min);
term = (term + (N / j) * (M / j) * (get(last) - get(j - 1)) % mod + mod) % mod;
}
ans[Query[judge].id] = term;
judge++;
}
for(int i = 1; i <= Q; i++)
{
printf("%lld\n", ans[i]);
}
return 0;
}