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

一道涉及莫比乌斯反演、树状数组、离线处理和极性函数的数表求和问题。题目要求计算一张数表中所有不超过a的数值之和,其中数表的每个元素由i和j的公约数决定。解决方案包括线性筛法计算莫比乌斯函数和约数和,离线处理数据,以及使用树状数组进行高效维护和求和,最终结果模2^31。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值