Gravatar
zcx
积分:273
提交:34 / 105

Pro4467  NOIP-T1-难度

by hl666:


奇思妙想题

首先考虑如果区间内存在某个质数P,则对于两个数x,y,除非w(x)=w(LCM(x,y))(即x对应的质因子集合为y对应的质因子集合的子集),否则不如用w(x)+1的代价直接把x和P连起来

因此现在的做法就很显然了,先把所有质因子集合有包含关系的点连起来,最后把每个连通块和P

连起来即可

有一种比较好的处理方法是,对于某个数x,我们令g(x)为它的质因数集合中所有数的乘积(由于有去重,因此g(12)=2×3=6;g(27)=3)

此时x对应的质因子集合为y对应的质因子集合的子集等价于g(x)是g(y)的约数,那么直接在上面跑一个调和级数的枚举即可

令M=∑ri,总复杂度O(MlogM)

但如果区间内没有质数怎么办呢,不难发现这样的区间长度一定不会很长,我们可以直接暴力跑生成树


#include<cstdio>

#include<iostream>

#include<algorithm>

#include<vector>

#include<utility>

#define RI register int

#define CI const int&

using namespace std;

typedef pair <int,int> pi;

const int N=1e6+5;

struct edge

{

int x,y,w;

inline edge(CI X=0,CI Y=0,CI W=0)

{

x=X; y=Y; w=W;

}

friend inline bool operator < (const edge& A,const edge& B)

{

return A.w<B.w;

}

}; int t,l,r,w[N],g[N],vis[N],sz[N],is_prime[N],fa[N];

inline void init(CI n)

{

RI i,j; for (i=1;i<=n;++i) g[i]=1;

for (i=2;i<=n;++i) if (!w[i])

{

is_prime[i]=1; g[i]=i; w[i]=1;

for (j=i*2;j<=n;j+=i) ++w[j],g[j]=g[j]*i;

}

}

inline int getfa(CI x)

{

return fa[x]!=x?fa[x]=getfa(fa[x]):x;

}

int main()

{

for (scanf("%d",&t),init(1e6);t;--t)

{

RI i,j; scanf("%d%d",&l,&r); int ans=0;

if (l==1)

{

for (i=2;i<=r;++i) ans+=w[i];

printf("%d\n",ans); continue;

}

bool has_prime=0;

for (i=l;i<=r;++i) if (is_prime[i]) has_prime=1;

if (has_prime)

{

for (i=1;i<=r;++i) sz[i]=vis[i]=0;

for (i=l;i<=r;++i) ++sz[g[i]];

for (i=2;i<=r;++i) if (!vis[i]&&sz[i])

{

ans+=w[i]*(sz[i]-1)+(w[i]+1); vis[i]=1;

for (j=i*2;j<=r;j+=i) if (!vis[j]&&sz[j])

vis[j]=1,ans+=w[j]*sz[j];

}

printf("%d\n",ans-2);

} else

{

vector <edge> E; for (i=l;i<=r;++i) fa[i]=i;

for (i=l;i<=r;++i) for (j=l;j<=r;++j)

E.push_back(edge(i,j,w[i]+w[j]-w[__gcd(i,j)]));

sort(E.begin(),E.end());

for (auto [x,y,w]:E)

{

if (getfa(x)==getfa(y)) continue;

ans+=w; fa[getfa(x)]=getfa(y);

}

printf("%d\n",ans);

}

}

return 0;

}





2026-09-04 16:12:18    
我有话要说
暂无人分享评论!