[硫化铂]卿且去

卿且去

在这里插入图片描述
在这里插入图片描述

题解

首先有结论,在 ( ⌊ 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],kN+,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=1nμ(i)(in2in)2Pπ(i)=2P(in2in)μ(i)2π(i)对于上式,我们发现 ( ⌊ n i ⌋ − ⌊ n 2 i ⌋ ) (\lfloor\frac{n}{i}\rfloor-\lfloor\frac{n}{2i}\rfloor) (in2in)是可以整除分块的,而 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=1dnf(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;
}

谢谢!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值