【UOJ221】【BZOJ4652】【NOI2016】循环之美(莫比乌斯反演,杜教筛)

本文针对NOI2016竞赛题目“循环之美”,详细解析了如何通过杜教筛算法来解决该问题。文章介绍了题目的背景及核心概念,并逐步展示了算法推导过程,最终给出了一种高效的解决方案。

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

Description

牛牛是一个热爱算法设计的高中生。在他设计的算法中,常常会使用带小数的数进行计算。牛牛认为,如果在 k 进制下,一个数的小数部分是纯循环的,那么它就是美的。现在,牛牛想知道:对于已知的十进制数 n 和 m,在 kk 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 xy 表示,其中 1≤x≤n,1≤y≤m,且 x,y是整数。一个数是纯循环的,当且仅当其可以写成以下形式:a.c1˙c2c3…cp-1cp˙其中,a 是一个整数,p≥1;对于 1≤i≤p,ci是 kk 进制下的一位数字。例如,在十进制下,0.45454545……=0.4˙5˙是纯循环的,它可以用 5/11、10/22 等分数表示;在十进制下,0.1666666……=0.16˙则不是纯循环的,它可以用 1/6 等分数表示。需要特别注意的是,我们认为一个整数是纯循环的,因为它的小数部分可以表示成 0 的循环或是 k-1 的循环;而一个小数部分非 0 的有限小数不是纯循环的。

Solution

感觉这题的题面比较巧妙,至少一眼看不出是杜教筛。。。

有几个地方推不动了,果然太菜了,,不看题解根本做不出来啊。。。

XJB猜结论发现题目其实就是求:

i=1nj=1m[(i,j)=1][(j,k)=1]∑i=1n∑j=1m[(i,j)=1][(j,k)=1]

然后颓柿子:
=j=1m[(j,k)=1]i=1n[(i,j)=1]=∑j=1m[(j,k)=1]∑i=1n[(i,j)=1]

=j=1m[(j,k)=1]i=1nd|(i,j)μ(d)=∑j=1m[(j,k)=1]∑i=1n∑d|(i,j)μ(d)

=d=1nμ(d)j=1md[(jd,k)=1]nd=∑d=1nμ(d)∑j=1⌊md⌋[(jd,k)=1]⌊nd⌋

=d=1n[(d,k)=1]μ(d)ndj=1md[(j,k)=1]=∑d=1n[(d,k)=1]μ(d)⌊nd⌋∑j=1⌊md⌋[(j,k)=1]

g(x)=xi=1[(i,k)=1]g(x)=∑i=1x[(i,k)=1],那么有g(x)=xkg(k)+g(xmodk)g(x)=⌊xk⌋g(k)+g(xmodk)
有了这个后,继续颓:
=d=1n[(d,k)=1]μ(d)ndg(md)=∑d=1n[(d,k)=1]μ(d)⌊nd⌋g(⌊md⌋)

然后我就不会做了。。。以前没怎么遇到过这种递归解决的题目,真的挺巧(sang4)
(bing4)的。

我们设f(n,k)=nd=1[(d,k)=1]μ(d)f(n,k)=∑d=1n[(d,k)=1]μ(d)k=pkqk=pkqpp为质数且(p,q)=1,那么有:

f(n,k)=f(n,q)d=1np[(dp,q)=1]μ(dp)f(n,k)=f(n,q)−∑d=1⌊np⌋[(dp,q)=1]μ(dp)

(减去与q互质单不和p互质的数的贡献)

f(n,k)=f(n,q)d=1np[(d,q)=1]μ(dp)f(n,k)=f(n,q)−∑d=1⌊np⌋[(d,q)=1]μ(dp)

(p,q)=1(p,q)=1

=f(n,q)+d=1np[(d,k)=1]μ(d)=f(n,q)+∑d=1⌊np⌋[(d,k)=1]μ(d)

(计入贡献dd一定和p互质(因为如果不互质则μ(dp)=0μ(dp)=0)。μ(dp)μ(dp)中除掉一个pp后反号)

即:

f(n,k)=f(n,q)f(np,k)

直接整除分块并递归求解,其中μμ前缀和用杜教筛即可。

Source

/**************************************
 * Au: Hany01
 * Prob: [BZOJ4652][NOI2016] 循环之美
 * Date: Mar 29th, 2018
 * Email: hany01@foxmail.com
**************************************/

#include<bits/stdc++.h>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>

using namespace std;
using namespace __gnu_pbds;

typedef long long LL;
typedef pair<int, int> PII;
typedef vector<int> VI;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define rep(i, j) for (register int i = 0, i##_end_ = j; i < i##_end_; ++ i)
#define For(i, j ,k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define SZ(a) ((int)(a.size()))
#define ALL(a) a.begin(), a.end()
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define Mod (1000000007)
#define y1 wozenmezhemecaia 
#ifdef hany01
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif

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

inline int read() {
    register char c_; register int _, __;
    for (_ = 0, __ = 1, c_ = getchar(); !isdigit(c_); c_ = getchar()) if (c_ == '-')  __ = -1;
    for ( ; isdigit(c_); c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxn = 100000005, maxk = 2005, maxN = 100005;

int n, m, k, mu[maxN], pr[maxN], cnt, np[maxN], md[maxN], G[maxk], N, md1[maxN];
LL Ans;
cc_hash_table<LL, LL> ht;
cc_hash_table<int, LL> ht_;

inline void getmu()
{
    mu[1] = 1;
    For(i, 2, N) {
        if (!np[i])
            pr[++ cnt] = i, mu[i] = -1, md[i] = i, md1[i] = 1;
        for (register int j = 1; j <= cnt && pr[j] * i <= N; ++ j) {
            np[i * pr[j]] = 1, md[i * pr[j]] = pr[j];
            if (!(i % pr[j])) { mu[i * pr[j]] = 0, md1[i * pr[j]] = md1[i]; break; }
            mu[i * pr[j]] = -mu[i], md1[i * pr[j]] = i;
        }
    }
    For(i, 2, N) mu[i] += mu[i - 1];
}

inline void getg()
{
    For(i, 1, k) G[i] = G[i - 1] + (__gcd(i, k) == 1);
}

inline LL g(int x) { return x / k * G[k] + G[x % k]; }

LL S(int n)
{
    if (n < N) return mu[n];
    if (ht_[n]) return ht_[n];
    LL Ans = 1;
    for (int l = 2, r; l <= n; l = r + 1)
        r = n / (n / l), Ans -= (LL)(r - l + 1) * S(n / l);
    return ht_[n] = Ans;
}

LL f(int n, int k)
{
    if (!n) return 0;
    if (k == 1) return S(n);
    register LL tmp = (LL)k * (LL)maxn + (LL)n;
    if (!ht[tmp]) ht[tmp] = f(n, md1[k]) + f(n / md[k], k);
    return ht[tmp];
}

int main()
{
#ifdef hany01
    File("bzoj4652");
#endif

    //Input
    n = read(), m = read(), k = read();

    N = 100000;
    getmu();

    getg();

    int nm = min(n, m);
    for (int l = 1, r; l <= nm; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        Ans += (f(r, k) - f(l - 1, k)) * (LL)(n / l) * g(m / l);
    }

    cout << Ans << endl;

    return 0;
}
//坡谓西湖,正如西子,浓抹淡妆临镜台。
//    -- 刘过《六州歌头·寄稼轩承旨》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值