NOI2016 循环之美 题解
1. 题意
求在
2. 模型转换
首先,
然后,题目要求不相等的纯循环小数个数。我们不妨用最简分数来代表一类相等的小数,即令
那么题目可以转化为求
3. 公式推导
首先对内层和式莫比乌斯反演。
设
带下取整的部分可以整除分块了,现在只需快速求出
前者比较好求,有
因为
而对于后者,设
边界为
4. 代码
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXK=2005;
const int MAXL=1000005;
struct Hash {
static const int SIZE=5000000;
static const int MOD=19260817;
static const int BASE=6662333;
static int hsh(int n, int k) {
return (1ll*n*BASE+k)%MOD;
}
int cnt, x[SIZE], y[SIZE], v[SIZE];
int head[MOD], nxt[SIZE];
int& operator () (int n, int k) {
int t=hsh(n, k), i=head[t];
while (i&&(x[i]!=n||y[i]!=k)) i=nxt[i];
if (!i) {
i=++cnt, x[i]=n, y[i]=k;
nxt[i]=head[t], head[t]=i;
}
return v[i];
}
};
int N, M, K, L, cp;
vector<int> fac[MAXK];
int mu[MAXL], pri[MAXL];
bool mark[MAXL];
int f[MAXK], g1[MAXL];
Hash g;
int F(int n) {
if (n<=K) return f[n];
return n/K*f[K]+F(n%K);
}
int G1(int n) {
if (n<=L) return g1[n];
int &res=g(n, 1);
if (res) return res;
res=1;
for (int i=2, j; i<=n; i=j+1) {
j=n/(n/i);
res-=(j-i+1)*G1(n/i);
}
return res;
}
int G(int n, int k) {
if (n==0) return 0;
if (k==1) return G1(n);
int& res=g(n, k);
if (res) return res;
for (int d: fac[k])
res+=mu[d]*mu[d]*G(n/d, d);
return res;
}
int main() {
// freopen("P1587.in", "r", stdin);
// freopen("P1587.out", "w", stdout);
scanf("%d%d%d", &N, &M, &K);
for (int i=1; i<=K; i++) if (K%i==0)
for (int j=i; j<=K; j+=i)
fac[j].push_back(i);
for (int i=1; i<=K; i++)
f[i]=f[i-1]+(__gcd(i, K)==1);
L=max((int)pow(min(N, M), 2.0/3), K);
g1[1]=mu[1]=1;
for (int i=2; i<=L; i++) {
if (!mark[i]) pri[++cp]=i, mu[i]=-1;
for (int j=1; j<=cp&&i*pri[j]<=L; j++) {
mark[i*pri[j]]=1;
if (i%pri[j]==0) { mu[i*pri[j]]=0; break; }
mu[i*pri[j]]=-mu[i];
}
g1[i]=g1[i-1]+mu[i];
}
long long ans=0;
for (int i=1, j; i<=N&&i<=M; i=j+1) {
j=min(N/(N/i), M/(M/i));
ans+=1ll*(G(j, K)-G(i-1, K))*(N/i)*F(M/i);
}
printf("%lld\n", ans);
return 0;
}
好强诶QAQ