卿且去
题解
首先有结论,在
(
⌊
n
2
⌋
,
n
]
(\lfloor \frac{n}{2}\rfloor,n]
(⌊2n⌋,n]中的所有数都被选择肯定是一组最优解。
我们可以考虑把原先的数分成
[
1
,
⌊
n
2
⌋
]
[1,\lfloor\frac{n}{2}\rfloor]
[1,⌊2n⌋]与
(
⌊
n
2
⌋
,
n
]
(\lfloor\frac{n}{2}\rfloor,n]
(⌊2n⌋,n]两部分,如果将倍数关系看成连边的话,可以发现,在第二部分的子图中不存在任何边,且第一部分中任何一个点都向着后部分至少一个点连边。
在这种情况下,我们的选择数相当于从图中选出一个独立集。
显然,对于不存在任何边的第二部分,肯定是一组合法解,我们现在要说明为什么它是最优解。
如果我们在第一部分中选择了集合
S
=
{
a
1
,
a
2
,
.
.
.
,
a
m
}
S=\{a_1,a_2,...,a_m\}
S={a1,a2,...,am},那么第二部分中至少有一个等大的集合不能选择。
因为
∀
x
∈
[
1
,
⌊
n
2
⌋
]
,
∃
k
∈
N
+
,
x
×
2
k
∈
(
⌊
n
2
⌋
,
n
]
\forall x\in [1,\lfloor\frac{n}{2}\rfloor],\exists k\in N^{+},x\times 2^k\in (\lfloor\frac{n}{2}\rfloor ,n]
∀x∈[1,⌊2n⌋],∃k∈N+,x×2k∈(⌊2n⌋,n],所以集合中每个
a
i
a_i
ai肯定在第二部分中有一个点
b
i
=
2
k
×
a
i
b_i=2^k\times a_i
bi=2k×ai。
由于
S
S
S中的任意两个数都不呈倍数关系,所以这些
b
i
b_i
bi都两两不同,也就是说我们在第二部分中至少有一个大小为
m
m
m的集合不能选择。
所以,我们在第一部分中一个都不选,第二部分全选,肯定是一组最优解。
考虑如何通过上面的结论计算答案,我们可以尝试计算当每个质数出现在集合中会使得多少个在
(
⌊
n
2
⌋
,
n
]
(\lfloor\frac{n}{2}\rfloor,n]
(⌊2n⌋,n]中的数可以被选择。
由于不同的两个质数可以使得同一个数被选择,所以显然需要容斥,记
P
P
P为质数集,
π
(
i
)
\pi(i)
π(i)表示
i
i
i的不同质因子个数。
A
n
s
=
∑
i
=
1
n
−
μ
(
i
)
(
⌊
n
i
⌋
−
⌊
n
2
i
⌋
)
2
∣
P
∣
−
π
(
i
)
=
−
2
∣
P
∣
∑
(
⌊
n
i
⌋
−
⌊
n
2
i
⌋
)
μ
(
i
)
2
−
π
(
i
)
Ans=\sum_{i=1}^{n}-\mu(i)(\lfloor\frac{n}{i}\rfloor-\lfloor\frac{n}{2i}\rfloor)2^{|P|-\pi(i)}=-2^{|P|}\sum_{}(\lfloor\frac{n}{i}\rfloor-\lfloor\frac{n}{2i}\rfloor)\mu(i) 2^{-\pi(i)}
Ans=i=1∑n−μ(i)(⌊in⌋−⌊2in⌋)2∣P∣−π(i)=−2∣P∣∑(⌊in⌋−⌊2in⌋)μ(i)2−π(i)对于上式,我们发现
(
⌊
n
i
⌋
−
⌊
n
2
i
⌋
)
(\lfloor\frac{n}{i}\rfloor-\lfloor\frac{n}{2i}\rfloor)
(⌊in⌋−⌊2in⌋)是可以整除分块的,而
f
(
i
)
=
μ
(
i
)
2
−
π
(
i
)
f(i)=\mu(i)2^{-\pi(i)}
f(i)=μ(i)2−π(i)又是一个积性函数。
于是我们很容易想到通过
m
i
n
25
min25
min25等方法去计算每个块里的
f
(
i
)
f(i)
f(i)之和。
由于像
m
i
n
25
min25
min25筛这种,他会将每个
∑
i
=
1
⌊
n
d
⌋
f
(
i
)
\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)
∑i=1⌊dn⌋f(i)的值都算出来,恰好契合我们的整除分块,我们可以在
O
(
n
3
4
ln
n
)
O\left(\frac{n^{\frac{3}{4}}}{\ln n}\right)
O(lnnn43)的时间中将它们都算出,所以是可以采取这种方法通过的。
时间复杂度
O
(
n
3
4
ln
n
)
O\left(\frac{n^{\frac{3}{4}}}{\ln n}\right)
O(lnnn43)。
建议采用递推的
m
i
n
25
min25
min25筛,由于需要算多点,递归版的需要记忆化,不太好搞,常数也大。
不过 偶耶 好像也有不用多点求值,只用算单点的方法。
源码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 400005
#define MAXM 50000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+3;
const int inv2=499122177;
const int fiv2=499122176;
const int jzm=2333;
const int zero=2000;
const int M=100000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
LL n,a[MAXN],n1;bool oula[MAXN];
int prime[MAXN],cntp,g[MAXN],h[MAXN],id1[MAXN],id2[MAXN],ans,tott;
void init(){
for(int i=2;i<=n1;i++){
if(!oula[i])prime[++cntp]=i;
for(int j=1;j<=cntp&&1ll*i*prime[j]<=n1;j++){
oula[i*prime[j]]=1;
if(i%prime[j]==0)continue;
}
}
}
int Id(LL x){return x<=n/x?id1[x]:id2[n/x];}
signed main(){
//freopen("yyds.in","r",stdin);
//freopen("yyds.out","w",stdout);
read(n);n1=sqrt(n);init();LL now=0,las=0;
for(LL l=1,r;l<=n;l=r+1){
r=n/(n/l);a[++tott]=n/l;
h[tott]=(a[tott]-1)%mo;
g[tott]=(a[tott]-1)%mo*fiv2%mo;
if(n/l<=l)id1[n/l]=tott;else id2[l]=tott;
}
for(int j=1;j<=cntp;j++)
for(int i=1;i<=tott&&1ll*prime[j]*prime[j]<=a[i];i++){
int k=Id(a[i]/prime[j]);
Add(g[i],add(mo-g[k],1ll*(j-1)*fiv2%mo,mo),mo);
Add(h[i],mo-h[k]+j-1,mo);
}
for(int j=cntp;j>0;j--)
for(int i=1;i<=tott&&1ll*prime[j]*prime[j]<=a[i];i++){
int k=Id(a[i]/prime[j]);
Add(g[i],1ll*fiv2*add(g[k],mo-1ll*fiv2*j%mo,mo)%mo,mo);
}
for(LL l=2,r;l<=n;l=r+1,las=now){
r=n/(n/l),now=g[Id(r)],
Add(ans,1ll*(n/l-n/2/l)%mo*add(now,mo-las,mo)%mo,mo);
}
printf("%d\n",(mo-1ll*qkpow(2,h[Id(n)]%(mo-1),mo)*ans%mo)%mo);
return 0;
}