线性筛(欧拉筛) 例题

325 阅读3分钟

Codeforces Round #304 (Div. 2) D. Soldier and Number Game

题目链接

思路

对于某一个数nn,要想玩最多回合,就每次取其一个质因数,直到取完,因此答案就是nn质因数的个数。由此可知,对于n!n!,答案就是1n1\sim n所有数字质因数个数的总和。因此只要提前维护出质因数个数的前缀和就可以轻松算出答案。

怎么求质因数的个数呢?对于一个合数nn,假设有一个比它更小的合数mm以及一个质数pp,使得n=m×pn=m\times p,那么就可以用mmpp的质因数个数相加得到nn的质因数个数。这个过程和欧拉筛的过程是一致的,因此可以在筛的过程中计算出当前被筛掉的合数的质因数个数。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void say() {}
void say_() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void say(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	say(oth...);
}
template <typename T, typename... T2>
inline void say_(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	say_(oth...);
}
/*#################################*/
const ll N=5E6+10;
ll n,l,r;
ll f[N],sum[N],vis[N];
vector<ll> pri;
void sieve()
{
	rep(i,2,5E6)
	{
		if(!vis[i])
		{
			pri.emplace_back(i);
			f[i]=1;
		}
		for(auto k:pri)
		{
			if(i*k>5e6)
				break;
			vis[i*k]=1;
			f[i*k]=f[i]+f[k];
			if(i%k==0)
				break;
		}
	}
}
int main()
{
	sieve();
	rep(i,2,5e6)
		sum[i]=sum[i-1]+f[i];
	read(n);
	while(n--)
	{
		read(r,l);
		say(sum[r]-sum[l]);
	}
}

2021年“图森未来杯”全国程序设计邀请赛 D题

题目链接

思路

对原式i=l1r1ii=l2r2i\prod\limits_{i=l_1}^{r_1}i\mid\prod\limits_{i=l_2}^{r_2}i变形得到(l21)!r1!(l11)!r2!(l_2-1)!\cdot r_1!\mid (l_1-1)!\cdot r_2!,因此转化成了判断阶乘是否能够整除的问题。设关于质数pp的函数fp(x)=yf_p(x)=y,使得pyn,py+1np^y\mid n,p^{y+1}\nmid n。当且仅当对于任意质数pp,都有fp(n!)fp(m!)f_p(n!)\geq f_p(m!)时,n!m!n!\mid m!

给定p,np,n,如何求fp(n!)f_p(n!)呢?因为pp是质数,因此只有pp的倍数才会对yy产生贡献。每个pp的倍数会产生11的贡献,每个p2p^2的倍数会产生22的贡献,但由于pp的倍数与p2p^2的倍数重合,因此p2p^2的倍数只会额外产生11的贡献,以此类推(具体可参考《具体数学》第四章)。而xxnn内的倍数共有nx\lfloor\frac{n}{x}\rfloor个,因此fp[n]=k1npkf_p[n]=\sum\limits_{k\geq 1}\lfloor\frac{n}{p^k}\rfloor

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void say() {}
void say_() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void say(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	say(oth...);
}
template <typename T, typename... T2>
inline void say_(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	say_(oth...);
}
/*#################################*/
const ll N=1E7+10;
ll a,b,c,d;
ll vis[N];
vector<ll> pri;
void sieve()
{
	rep(i,2,1e7)
	{
		if(!vis[i])
		{
			pri.emplace_back(i);
			vis[i]=1;
		}
		for(auto k:pri)
		{
			if(i*k>1e7)
				break;
			vis[i*k]=1;
			if(i%k==0)
				break;
		}
	}
}
ll fp(ll x,ll p)
{
	return x==0?0:fp(x/p,p)+x/p;
}
void solve()
{
	read(a,b,c,d);
	for(auto k:pri)
	{
		ll bc=fp(b,k)+fp(c-1,k),ad=fp(a-1,k)+fp(d,k);
		if(bc>ad)
		{
			puts("NO");
			return;
		}
	}
	puts("YES");
}
int main()
{
	sieve();
	int _;
	cin>>_;
	while(_--)
	{
		solve();
	}
}