BZOJ3259(莫比烏斯反演 + 樹狀數組 + 離散處理 + 極性函數)

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章