背景:
YH
\text{YH}
YH将它出成了一道模拟赛题,写了个
Θ
(
n
log
n
)
\Theta(n \log n)
Θ(nlogn)的,主要是没有发现我的容斥系数为
μ
\mu
μ,蠢了,只有
50pts
\text{50pts}
50pts,还是最高???
题目传送门:
https://www.luogu.org/problem/P5218
题意:
n
n
n个数
[
1
,
n
]
[1,n]
[1,n],每一次可以选任意的数,选出的数每一个数都可以乘上一个整数。问能表示出
(
−
∞
,
+
∞
)
(-\infty,+\infty)
(−∞,+∞)的选法的方案数。
思路:
容易想到合法的情况一定能表示出
1
1
1,因此形如将
a
x
+
b
y
+
c
z
+
.
.
.
=
1
ax+by+cz+...=1
ax+by+cz+...=1的不等式解出来(
x
,
y
,
z
x,y,z
x,y,z为你选进来的数,
a
,
b
,
c
a,b,c
a,b,c表示你分别乘上的数),那么有裴蜀定理可知
gcd
(
x
,
y
,
z
,
.
.
.
)
=
1
\gcd(x,y,z,...)=1
gcd(x,y,z,...)=1。
那么问题转化成选出任意多的数,使它们的
gcd
=
1
\gcd=1
gcd=1。
设
f
i
f_i
fi表示
gcd
\gcd
gcd为
i
i
i时的方案数,
F
i
F_i
Fi表示选出的数都是
i
i
i的倍数的方案数。
考虑每一个数选或不选,一共
⌊
n
i
⌋
\lfloor\frac{n}{i}\rfloor
⌊in⌋个数,去掉全部都不选的
1
1
1种情况,答案即为
2
⌊
n
i
⌋
−
1
2^{\lfloor\frac{n}{i}\rfloor}-1
2⌊in⌋−1。
枚举
gcd
\gcd
gcd,考虑容斥,显然有
f
gcd
=
∑
gcd
∣
d
,
d
≤
n
μ
d
F
d
f_{\gcd}=\sum_{\gcd|d,d≤n}\mu_dF_d
fgcd=∑gcd∣d,d≤nμdFd。
证明就是一个容斥,考虑你当前选出的数可能有更大的
gcd
\gcd
gcd,被计算多次,容斥一下就
OK
\text{OK}
OK了。
a
n
s
=
f
1
=
∑
i
=
1
n
μ
i
(
2
⌊
n
i
⌋
−
1
)
ans=f_1=\sum_{i=1}^{n}\mu_i(2^{\lfloor\frac{n}{i}\rfloor}-1)
ans=f1=i=1∑nμi(2⌊in⌋−1)
发现 ⌊ n i ⌋ \lfloor\frac{n}{i}\rfloor ⌊in⌋可以整除分块,杜教筛 μ \mu μ,整除分块即可。
原谅我水题不能切,题解还写得长,其实是我菜。
代码:
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
#define LL long long
#define mod 1000000007
using namespace std;
map<LL,int> MAP;
int prime[10000010],mu[10000010],sum_mu[10000010];
bool bz[10000010];
LL n;
void init(int ma)
{
int t=0;
bz[0]=bz[1]=true;
mu[1]=1;
for(int i=2;i<=ma;i++)
{
if(!bz[i]) prime[++t]=i,mu[i]=-1;
for(int j=1;j<=t&&i*prime[j]<=ma;j++)
{
bz[i*prime[j]]=true;
if(!(i%prime[j]))
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=ma;i++)
sum_mu[i]=(sum_mu[i-1]+mu[i]+mod)%mod;
}
int ksm(int x,LL k)
{
int tot=1;
for(;k;k>>=1)
{
if(k&1) tot=(LL)tot*x%mod;
x=(LL)x*x%mod;
}
return tot;
}
int calc_mu(LL x)
{
if(x<=(int)1e7) return sum_mu[x];
if(MAP[x]) return MAP[x];
int ans=1;
for(LL l=2,r;l<=x;l=r+1)
{
r=x/(x/l);
ans=((LL)ans-((LL)r-l+1)*calc_mu(x/l)%mod+mod)%mod;
}
return MAP[x]=ans;
}
int work(LL n)
{
int ans=0;
for(LL l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
ans=((LL)ans+(((LL)calc_mu(r)-calc_mu(l-1)+mod)%mod)*(((LL)ksm(2,n/l)-1+mod)%mod)%mod)%mod;
}
return ans;
}
int main()
{
scanf("%lld",&n);
init((int)min((LL)(1e7),n));
printf("%d",work(n));
}