ICPC 2018 南京网赛 Easy Math
标签
- 递归式
- 杜教筛
前言
- 做了这题,感觉自己又学到了好多东西~
- 我的csdn和博客园是同步的,欢迎来访danzh-博客园~
简明题意
- 求
思路
- 推式子吧~
- 首先莫比乌斯函数,一旦含有平方因子,他的值就是-1.所以我们可以先特判一下n含不含平方因子,含的话,答案就是0。
- 然后我们看看n不含平方因子的情况。既然n没有平方因子,那么显然n是可以分解成
的。我们任取其中一个质数p。然后把原式写成:
- 想一想鸡性函数性质(此处是积不是鸡,但是鸡太美了所以我就对鸡和积不加区分好了2333),鸡性函数
,如果ab互质,则有
,所以我们回到原来的式子,把p提出来,是不是就成了
- 显然不是的,因为那个鸡性函数性质,是需要互质的。而p和i*n/p不一定互质鸭!但是我们先不管这个,待会再考虑这个。假设他俩互质了,一个质数的莫比乌斯函数值一定是-1,我们直接把-1提前,所以原式:
与索引无关的量可以提到和式前面(索引是指上式中的i)
- 然后我们再回头考虑之前的不互质的情况。p和 不互质,而p与n/p互质,可以推出p|i。(这一步很显然,可以自己推一下)。也就是说,对于原式,上面的式子多减去了不互质的情况,而且还少加上了不互质的情况。对于多减去的部分,我们直接加回去就好了。但是少加上的部分呢?少加上的部分是少加上了 ,这样的话是不是发现 又有平方因子了呢,所以少加上的值我们不用管,因为他就是0.我们只用关心多减去的部分,加上就好了。
- 我们把多减去的加回去,显然原式就是:
- 然后关注后面的那个式子,看到后面的条件
,是不是立马想到更换枚举上限了?所以原式就成了:
- 我们另
,于是,我们终于得到了想要的递归式:
- 这个二元组的边界条件,就是S(m,1)和S(0,n)。然而mn太大,所以边界直接杜教筛!
注意事项
- 计算n是否含平方因子,这个时候又prime[i[*prime[i],注意别溢出!
总结
- 一个数没有平方因子等价于
- 我们在计算鸡性函数是,如果计算的是 ,也就是自变量中含有常数,我们可以尝试把这个常数分解成 ,从而可以利用鸡性函数性质,从而进一步转化为递归式
- 与索引无关的量可以提到和式前面
- ,如果a与c互质,那么
- 对于一个和式,如果索引必须是某个数的倍数,那么可以直接更换上限,然后扩大索引。比如 ,他等价于 (其中i缩小到了原来的p倍,需要乘上)
- 对于一个二元递归式,边界条件应该是每一元都到边界
AC代码
#include<cstdio>
#include<unordered_map>
using namespace std;
const int maxn = 1e7 + 10;
long long n, m;
int prime[maxn], min_p[maxn], mu[maxn], pre_mu[maxn];
bool no_prime[maxn];
int shai(int n)
{
int cnt = 0;
no_prime[1] = mu[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!no_prime[i])
prime[++cnt] = i, mu[i] = -1, min_p[i] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; j++)
{
no_prime[prime[j] * i] = 1;
mu[prime[j] * i] = i % prime[j] == 0 ? 0 : -mu[i];
min_p[prime[j] * i] = prime[j];
if (i % prime[j] == 0) break;
}
}
for (int i = 1; i <= n; i++)
pre_mu[i] = pre_mu[i - 1] + mu[i];
return cnt;
}
unordered_map<int, int> rec_mu;
int cal(int n)//n的莫比乌斯函数前缀和
{
if (n <= maxn - 10) return pre_mu[n];
if (rec_mu[n]) return rec_mu[n];
int l = 2, r, ans = 1;
while (l <= n)
{
r = n / (n / l);
ans -= (r - l + 1) * cal(n / l);
l = r + 1;
}
return rec_mu[n] = ans;
}
long long S(long long m, long long n)
{
if (m == 0) return 0;
if (n == 1) return cal(m);
for (int i = 1; 1ll * prime[i] * prime[i] <= n; i++)
{
if (n % prime[i] == 0)
return -S(m, n / prime[i]) + S(m / prime[i], n);
}
return -S(m, n / n) + S(m / n, n);
}
void solve()
{
int cnt = shai(maxn - 10);
scanf("%lld%lld", &m, &n);
for (int i = 1; 1ll * prime[i] * prime[i] <= n; i++)
if (n % (1ll * prime[i] * prime[i]) == 0)
{
printf("0\n");
return;
}
printf("%lld\n", S(m, n));
}
int main()
{
freopen("Testin.txt", "r", stdin);
solve();
return 0;
}