[SDOI2014]数表

题面就懒得放了,给个链接:LOJ
自己的想法:
\[ \begin{aligned} &\sum_{i=1}^n\sum_{j=1}^m\sigma(\gcd(i.j))\\ &=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|(i,j)}x\\ &=\sum_{x=1}^n\sum_{i=1}^{\frac{n}{x}}\sum_{j=1}^\frac{m}{x} \end{aligned} \]
然后就gg了,无法处理 \(\le a\) 的限制。
下面是正解(by Wolfycz):
2019-09-11 21-14-36 的屏幕截图.png

\(\sigma(P) = P +1\)
\(a\perp b,\)
\[ \begin{aligned} \sigma(ab)&=\sum_{x|ab}x\\ &=\sum_{x|a}\sum_{y|b}xy\\ &=\sigma(a)\sigma(b) \end{aligned} \]
\(a\)包含\(p\)这个质因子
\[ \sigma(pa) = \sigma(a) + \sigma(\frac{a}{low(a)})low(ap) \]
其中 \(low(x)\)\(x\) 最小质因子的指数次幂,若 \(x = \prod p_i^{c_i},p_1<p_2\cdots\) ,则 \(low(x) = p_1^{c_1}\)
然后就可以欧拉筛了。

\(\sigma\) 筛出来后,注意到 \(\sum_{d|T}\sigma(d)\mu(\frac{T}{d})\) 只有 \(\sigma(d)\le a\) 才会有贡献,所以我们把询问按 \(a\) 从小到大排序,然后把 \(\sigma(d) \le a\)\(\sigma(d)\mu(\frac{T}{d})\) 插入树状数组,枚举 \(d\) 的倍数,就可以做到 \(O(n\ln n\log n)\)

查询就数论分块+树状数组区间求和 \(O(q\sqrt n \log_2n)。\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <fstream>

#define uint unsigned int
typedef long long LL;
typedef unsigned long long uLL;

#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;

using namespace std;

inline void proc_status()
{
    ifstream t("/proc/self/status");
    cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}

template<class T> inline T read() 
{
    register T x(0);
    register char c;
    register int f(1);
    while (!isdigit(c = getchar())) if (c == '-') f = -1;
    while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
    return x * f;
}

template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = 1e5;

struct Query
{
    uint n, m, id;
    uint ans;
    unsigned int a;
} q[maxN + 2];

struct BIT
{
    unsigned int t[maxN + 2];
    void add(int x, unsigned int y) { for (; x <= maxN; x += x & -x) t[x] += y; }
    unsigned int query(int x) 
    {
        unsigned int ans = 0;
        for (; x; x -= x & -x)
            ans += t[x];
        return ans;
    }
} T;

int Q;
bool vis[maxN + 2];
vector<unsigned int> prime;
pair<unsigned int, unsigned int> sigma[maxN + 2];
unsigned int mu[maxN + 2], low[maxN + 2];

void Input()
{
    Q = read<int>();
    for (int i = 1; i <= Q; ++i) 
        q[i].n = read<int>(), q[i].m = read<int>(), q[i].a = read<int>(), q[i].id = i;
}

void Init()
{
    sigma[1] = MP(1, 1);
    mu[1] = 1;
    for (register int i = 2; i <= maxN; ++i)
    {
        if (!vis[i])
        {
            mu[i] = -1;
            low[i] = i;
            sigma[i] = MP(1 + i, i);
            prime.push_back(i);
        }
        for (register int j = 0; j < SZ(prime) && prime[j] * i <= maxN; ++j)
        {
            int t = prime[j] * i;
            vis[t] = 1;
            if (i % prime[j] == 0)
            {
                low[t] = low[i] * prime[j];
                mu[t] = 0;
                sigma[t] = MP(sigma[i].first + low[t] * sigma[i / low[i]].first, t);
            }
            else
            {
                low[t] = prime[j];
                mu[t] = -mu[i];
                sigma[t] = MP(sigma[i].first * sigma[prime[j]].first, t);
            }
        }
    }
    //DEBUG("%d\n", sigma[9].first);
    sort(sigma + 1, sigma + 1 + maxN);
}

bool cmp(const Query& A, const Query& B) { return A.a < B.a; }
bool cmp2(const Query& A, const Query& B) { return A.id < B.id; }

void Solve()
{
    sort(q + 1, q + 1 + Q, cmp);
    int pos = 0;
    for (int i = 1; i <= Q; ++i)
    {
        while (sigma[pos + 1].first <= q[i].a and pos < maxN)
        {
            pos++;
            for (int j = 1; j * sigma[pos].second <= maxN; ++j)
                T.add(sigma[pos].second * j, sigma[pos].first * mu[j]);
        }
        unsigned int ans = 0;
        if (q[i].n > q[i].m) swap(q[i].n, q[i].m);
        int n = q[i].n, m = q[i].m;
        for (register int l = 1, r; l <= n; l = r + 1)
        {
            r = min(n / (n / l), m / (m / l));
            ans += (T.query(r) - T.query(l - 1)) * (n / l) * (m / l);
        }
        q[i].ans = ans & 0x7FFFFFFF;
    }
    sort(q + 1, q + 1 + Q, cmp2);
    for (int i = 1; i <= Q; ++i)
        printf("%d\n", q[i].ans);
}

int main() 
{
#ifndef ONLINE_JUDGE
    freopen("xhc.in", "r", stdin);
    freopen("xhc.out", "w", stdout);
#endif
    Input();
    Init();
    Solve();
    return 0;
}

转载于:https://www.cnblogs.com/cnyali-Tea/p/11509925.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值