BZOJ3259(莫比乌斯反演 + 树状数组 + 离散处理 + 极性函数)

来源:互联网 发布:二手交易软件排名 编辑:程序博客网 时间:2024/06/06 00:31

3529: [Sdoi2014]数表

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 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

Sample Output

20
148

HINT

1 < =N.m < =10^5  , 1 < =Q < =2×10^4

Source

Round 1 Day 1


解题思路:这题是莫比乌斯反演里面比较难得一个,首先要反演半天,得到一个公式,然后还要离线处理,最后还要用一个树状数组维护一下,中间的莫比乌斯函数和约数和函数都是积性函数,所以都可以线性筛。


#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;}


阅读全文
1 0
原创粉丝点击