Tsinsen D461 Fraction

// 内部资料
传送门
考场上的思路
对于 60% 的数据

考虑解决 >ab 的个数。显然为:
ni=2(与 i 互质的分子数与 i 互质且分数小于等于 ab 的分子数)
前者为:
φ(i)
后者为:
jaibj=1[gcd(i,j)=1]
考虑求后者的和。后者等价于:
ni=2aibj=1[gcd(i,j)=1]

ni=2aibj=1didjμ(d)

枚举 d
ni=2diμ(d)aibdj=11

ni=2diμ(d)aibd

不妨求(结果不变):
ni=1diμ(d)aibd
再次枚举 d
d=1μ(d)ndi=1adibd

d=1μ(d)ndi=1aib

对右侧计算前缀和,然后使用数论分块,时间复杂度 O(n)

欧拉函数会被抵消,不用管。


最后单独考虑 =ab 的分数。若 nb,那么对答案有 1 的贡献,之前没有算上。

对于 100% 的数据

使用数论分块、类欧几里得、杜教筛。???不对。就是这样。

正解

对就是这样。

d=1μ(d)ndi=1aib

使用数论分块。对右边部分使用类欧几里得,对于左边使用杜教筛。

然而我太弱了,什么都不会,还是复习下吧。

杜教筛

现在我们要求:

ni=1μ(i)

杜教筛的思路是:找一个函数,对我们要求前缀和的函数做狄利克雷卷积,再来做前缀和(这个前缀和通常很好计算)。然后枚举因数,balabalabala……

我们知道:
μ1=ϵ

考虑:
1=ni=1diμ(d)

枚举因数,把要求前缀和的函数留在里面
nd=11ndi=1μ(i)nd=1S(nd)

所以有:
S(n)=1nd=2S(nd)

对右侧进行数论分块,递归求解。预处理 O(n23),开哈希表,注意 long long

类欧几里得

完全不会!!!我还是在纸上写算了。

参考代码

一定要注意先模再乘!

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0;
    bool positive = true;
    char ch = std::getchar();
    while (!(std::isdigit(ch) || ch == '-')) ch = std::getchar();
    if (ch == '-')
    {
        positive = false;
        ch = std::getchar();
    }
    while (std::isdigit(ch))
    {
        (a *= 10) -= ch - '0';
        ch = std::getchar();
    }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20];
    int length = 0;
    if (x < 0) putchar('-');
    else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int mod = 998244353;
LL n, a, b, c, d;
LL gcd(LL a, LL b)
{
    return b ? gcd(b, a % b) : a;
}
int T;
struct Query
{
    LL n, a, b, c, d;
    void read()
    {
        n = readIn();
        a = readIn();
        b = readIn();
        c = readIn();
        d = readIn();
    }
    void release()
    {
        ::n = n;
        ::a = a;
        ::b = b;
        ::c = c;
        ::d = d;
    }
} querys[5];

#define RunInstance(x) delete new x
struct brute
{
    brute()
    {
        for (int o = 1; o <= T; o++)
        {
            querys[o].release();
            int ans = 0;
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j < i; j++)
                {
                    if (a * i <= b * j && j * d <= c * i && gcd(i, j) == 1)
                    {
                        ans++;
                    }
                }
            }
            printOut(ans);
        }
    }
};
struct cheat
{
    static const int maxn = int(1e7);

    bool isntPrime[maxn + 5];
    int mu[maxn + 5];
    int prime[664580];

    void init()
    {
        prime[0] = 0;
        mu[0] = 0;
        mu[1] = 1;
        for (int i = 2; i <= int(1e7); i++)
        {
            if (!isntPrime[i])
            {
                prime[++prime[0]] = i;
                mu[i] = -1;
            }
            for (int j = 1, p = prime[j], s = i * p; j <= prime[0] && s <= int(1e7); j++, p = prime[j], s = i * p)
            {
                isntPrime[s] = true;
                if (i % p)
                    mu[s] = -mu[i];
                else
                {
                    mu[s] = 0;
                    break;
                }
            }
        }

        for (int i = 2; i <= int(1e7); i++)
            mu[i] += mu[i - 1];
    }

    LL sum[maxn + 5];

    LL calc(LL up, LL down)
    {
        LL ans = 0;

        sum[0] = 0;
        for (int i = 1; i <= n; i++)
            sum[i] = (sum[i - 1] + up * i / down) % mod;

        for (int i = 1, t; i <= n; i = t + 1)
        {
            t = n / (n / i);
            ans -= (mu[t] - mu[i - 1]) * sum[n / i];
        }
        return ans;
    }

    void solve()
    {
        printOut(((calc(a, b) - calc(c, d) + (n >= b)) % mod + mod) % mod);
    }

    cheat() : isntPrime()
    {
        init();

        for (int i = 1; i <= T; i++)
        {
            querys[i].release();
            solve();
        }
    }
};
struct work
{
    static const int maxn = int(1e7);

    bool isntPrime[maxn + 5];
    int mu[maxn + 5];
    int prime[664580];

    void init()
    {
        prime[0] = 0;
        mu[0] = 0;
        mu[1] = 1;
        for (int i = 2; i <= int(1e7); i++)
        {
            if (!isntPrime[i])
            {
                prime[++prime[0]] = i;
                mu[i] = -1;
            }
            for (int j = 1, p = prime[j], s = i * p; j <= prime[0] && s <= int(1e7); j++, p = prime[j], s = i * p)
            {
                isntPrime[s] = true;
                if (i % p)
                    mu[s] = -mu[i];
                else
                {
                    mu[s] = 0;
                    break;
                }
            }
        }

        for (int i = 2; i <= int(1e7); i++)
            mu[i] += mu[i - 1];
    }

    struct HashTable
    {
        static const int size = int(1e6) + 3;
        struct Node
        {
            ULL key;
            LL val;
            int next;
            Node() {}
            Node(ULL key, LL val) : key(key), val(val), next(-1) {}
        };
        int head[size];
        std::vector<Node> nodes;
        HashTable()
        {
            std::memset(head, -1, sizeof(head));
        }
        int query(ULL key) const
        {
            int cnt = head[key % size];
            while (~cnt)
            {
                const Node& t = nodes[cnt];
                if (t.key == key)
                    return cnt;
                cnt = t.next;
            }
            return -1;
        }
        void insert(ULL key, LL val)
        {
            int cnt = head[key % size];
            if (!~cnt)
            {
                head[key % size] = nodes.size();
                nodes.push_back(Node(key, val));
            }
            else
            {
                while (~nodes[cnt].next)
                {
                    if (nodes[cnt].key == key) throw;
                    cnt = nodes[cnt].next;
                }
                nodes[cnt].next = nodes.size();
                nodes.push_back(Node(key, val));
            }
        }
    } hash;

    LL Mu(LL n)
    {
        if (n <= LL(1e7)) return mu[n];
        LL ans = hash.query(n);
        if (~ans) return hash.nodes[ans].val;
        ans = 1;
        for (LL i = 2, t; i <= n; i = t + 1)
        {
            t = n / (n / i);
            ans = (ans - (t - i + 1) % mod * Mu(n / i)) % mod;
        }
        hash.insert(n, ans);
        return ans;
    }

    LL F(LL n)
    {
        if (n & 1) return (n + 1) / 2 % mod * (n % mod) % mod;
        return n / 2 % mod * ((n + 1) % mod) % mod;
    }
    LL Euclid(LL a, LL b, LL c, LL n)
    {
        if (a >= c || b >= c)
            return ((a / c) * F(n) % mod + (b / c) * (n + 1) % mod + Euclid(a % c, b % c, c, n)) % mod;
        if (!a)
            return 0;
        return ((a * n + b) / c % mod * n - Euclid(c, c - b - 1, a, (a * n + b) / c - 1)) % mod;
    }

    LL calc(LL up, LL down)
    {
        LL ans = 0;

        LL pre = 0;
        for (LL i = 1, t; i <= n; i = t + 1)
        {
            t = n / (n / i);
            LL cnt = Mu(t);
            ans -= (cnt - pre) % mod * Euclid(up, 0, down, n / i) % mod;
            ans %= mod;
            pre = cnt;
        }
        return ans;
    }

    void solve()
    {
        printOut(((calc(a, b) - calc(c, d) + (n >= b)) % mod + mod) % mod);
    }

    work() : isntPrime()
    {
        init();

        for (int i = 1; i <= T; i++)
        {
            querys[i].release();
            solve();
        }
    }
};

void run()
{
    T = readIn();
    for (int i = 1; i <= T; i++)
        querys[i].read();

    LL maxn = 0;
    for (int i = 1; i <= T; i++)
        maxn = std::max(maxn, querys[i].n);

    RunInstance(work);
}

int main()
{
#ifndef LOCAL
    freopen("fraction.in", "r", stdin);
    freopen("fraction.out", "w", stdout);
#endif
    run();
    return 0;
}
总结

基础不牢,地动山摇。

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80669489