CF585E Present for Vitalik the Philatelist

CF 585 E Present for Vitalik the Philatelist

我们假设$f(x)$表示与$x$互质的数的个数,$s(x)$为 gcd 为$x$的集合的个数。

那么显然答案就是

所以我们现在考虑怎么求$f$和$s$。

先考虑$f$,

我们设$t(x) = \sum_{x|i} c_i$,不难发现这就是我的 这篇博客 里面的第二种卷积,可以筛出来。

那么

然后又可以用第一种卷积来做,于是我们就跑出了$f$。

现在考虑怎么求$s$,我们可以假设$s’(x)$就是 gcd 为$x$的倍数的所有集合的个数。我们需要算出$x$的倍数的数字个数,就是$\sum_{x|i} c_i$,这个不就是前面的$t(x)$吗!

所以显然有

同时我们知道

这个东西就是第二个卷积的反过来的形式,也就是第四种卷积!

所以我们可以三次$O(w\log\log w)$跑过去啦。

开始看错$w$大小了。。MLE了两发。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "queue"
using namespace std;
#define MAXN 10000006
#define P 1000000007
int n;
int A[500006] , c[MAXN] , _[500006] , mu[MAXN] , s[MAXN];
int pri[MAXN] , en , lim;
void sieve( int x ) {
pri[0] = 1; mu[1] = 1;
for( int i = 2 ; i <= x ; ++ i ) {
if( !pri[i] ) pri[++ en] = i , mu[i] = -1;
for( int j = 1 ; i * pri[j] <= x && j <= en ; ++ j ) {
pri[i * pri[j]] = 1;
if( i % pri[j] == 0 ) { mu[i * pri[j]] = 0; break; }
mu[i * pri[j]] = -mu[i];
}
}
}
signed main() {
// freopen("in","r",stdin);
cin >> n;
for( int i = 1 ; i <= n ; ++ i ) scanf("%d",&A[i]) , ++ c[A[i]] , lim = max( lim , A[i] );
sieve( lim );
for( int i = 1 ; i <= en ; ++ i )
for( int j = lim / pri[i] ; j ; -- j )
c[j] += c[j * pri[i]];
for( int i = 1 ; i <= lim ; ++ i ) mu[i] *= c[i];
for( int i = 1 ; i <= en ; ++ i )
for( int j = 1 ; j * pri[i] <= lim ; ++ j )
( mu[j * pri[i]] += mu[j] ) %= P;
_[0] = 1;for( int i = 1 ; i < 500006 ; ++ i ) _[i] = _[i - 1] * 2 % P;
for( int i = 1 ; i <= lim ; ++ i ) s[i] = _[c[i]] - 1;
for( int i = en ; i ; -- i )
for( int j = 1 ; j * pri[i] <= lim ; ++ j )
( s[j] -= s[j * pri[i]] ) %= P;
int ans = 0;
for( int i = 2 ; i <= lim ; ++ i ) ( ans += 1ll * s[i] * mu[i] % P ) %= P;
cout << ( ans + P ) % P << endl;
}
文章作者: yijan
文章链接: https://yijan.co/old49/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog