NEFU 大一寒假训练十(素数筛)2020.02.14

Summary

今天的题一点也不友好(好吧,是我太菜了) /(ㄒoㄒ)\~~
昨天 题目预测 准确率 所有题目:6/10=60% 预测题目:6/7=85.71%
感觉预测被发现针对了(包括但不限于加强数据,增加新题…),瑟瑟发抖 ing
早上临时加题了,昨天数据又被疯狂强化了一波,感觉到了不是很友好的气息 ~
一大早上看到 root 正在疯狂提交就知道估计多半是废了
在这里插入图片描述
▲ 疯狂提交的 root 和瑟瑟发抖的我 → (@_@)

以下关于 素数筛 筛素数 这类的代码就不另外说明了
详情请参考以前的一篇文章:C/C++ 素数筛 ACM算法

Information

No. Title AC/Submit
A 纯素数 15/122
B 半素数 11/50
C 函数版素数判定 43/241
D 最大素因子 15/33
E 素数与数论 9/59
F 差点是素数 0/3
G 高木同学的因子 27/38
H 知否知否,应是绿肥红瘦 48/102
I 素数线性筛 21/65
J 五十弦翻塞外声 4/20

Problem A: 纯素数 (586) [15/122]

Tips

这个 A 题主要就是怎么去掉高位,筛完以后判断素数难度不大
可以先获取位数再获取最高位数字然后相乘,写成函数同时返回两个值

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 1000001
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

pair<int,int> getws(int n)
{
	int cnt=1;
	while(n/10)
	{
		cnt++;
		n/=10;
	}
	return make_pair(cnt,n);
}

int main()
{
	getPrime();
	int n,ok,tmp;
	pair<int,int> ret;
	while(~scanf("%d",&n))
	{
		ok=1;
		while(n>0)
		{
			ret=getws(n);
			if(prime[n])n-=pow(10,ret.first-1)*ret.second;
			else
			{
				ok=0;
				break;
			}
		}
		if(ok)printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

Problem B: 半素数 (587) [11/50]

Tips

思路:不断的用素数去拆这个数,能拆就拆,
最后看总共拆成了几个数,正好是2个输出 YES 否则输出 NO。

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 10001
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

int main()
{
	getPrime();
	int n,cnt;
	while(~scanf("%d",&n))
	{
		cnt=0;
		for(int i=0;i<num&&primeList[i]<=sqrt(n);i++)
		{
			if(n%primeList[i]==0)
			{
				cnt++;
				n/=primeList[i];
				i=-1;
			}
		}
		if(n!=1)cnt++;
		if(cnt==2)printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

Problem C: 函数版素数判定 (825) [43/241]

Tips

可以算是素数筛里比较基础的模(song)板(fen)题了吧。

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 10000001
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

int main()
{
	getPrime();
	int n;
	while(~scanf("%d",&n))
	{
		if(prime[n])printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

Problem D: 最大素因子 (585) [15/33]

Tips

可以先找到比这个数大的素数,再依次向前查找素数表中的素数是否为它的因子
关于 upper_bound 用法详见 C/C++ ACM 二分查找算法

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 1000001
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

int main()
{
	getPrime();
	int n,pos,ans;
	while(~scanf("%d",&n))
	{
		if(n==1)
		{
			cout<<0<<endl;
			continue;
		}
		pos=upper_bound(primeList,primeList+num,n)-primeList-1;
		for(int i=pos;i>=0;i--)
		{
			if(n%primeList[i]==0)
			{
				printf("%d\n",i+1);
				break;
			}
		}
	}
	return 0;
}

Problem E: 素数与数论 (781) [9/59]

Tips

直接 lower_bound 确定是第几个素数,然后相减即可。
注意:不要忘了处理边界为素数的情况。
关于 lower_bound 的用法也在这里:C/C++ ACM 二分查找算法

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 1000001
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

int main()
{
	getPrime();
	int x,y,cnt,pos1,pos2;
	while(~scanf("%d %d",&x,&y))
	{
		cnt=0;
		pos1=lower_bound(primeList,primeList+num,x)-primeList;
		pos2=lower_bound(primeList,primeList+num,y)-primeList;
		if(prime[y])pos2++;
		printf("%d\n",pos2-pos1);
	}
	return 0;
}

Problem F: 差点是素数 (1321) [0/3]

Tips

比赛时:
差点是素数,差点就做出来了 /(ㄒoㄒ)\~~
样例都过了,就是疯狂 TLE /(ㄒoㄒ)\~~ x2

补题更新:
看了题解发现题解竟然又打了个表,我原来的那个读进去再算就来不及了
素数筛的部分就不说了,第二张表打的是 [1,1e12] 里满足条件的所有数
我以为会很多没敢打表,结果竟然没多少可以打表来做,amazing ~
有了第二张表就可以利用 lower_boundupper_bound 快速计算了。

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 1000001
char prime[MAXN];
long long primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

long long pre[MAXN],pnum=0;
void getAns()
{
    for(long long i=0;i<num;i++)
        for(long long j=primeList[i]*primeList[i];j<=1e12;j*=primeList[i])
            pre[pnum++]=j;
    sort(pre,pre+pnum);
}

long long countans(long long l,long long r)
{
	
    long long pos1=lower_bound(pre,pre+pnum,l)-pre;
    long long pos2=upper_bound(pre,pre+pnum,r)-pre;
    return pos2-pos1;
}

int main()
{
	getPrime();
	getAns();
	int t,hb=1;
	long long l,r,l1,r1,res;
	scanf("%d",&t);
	while(t--)
	{
		res=0;
		hb=1;
		scanf("%lld %lld %lld %lld",&l,&r,&l1,&r1);
		if(l1>=l&&r1>=r&&l1<=r)r=r1;
		else if(l1<=l&&r1<=r&&r1>=l)l=l1;
		else if(l<=l1&&r>=r1);
		else if(l1<=l&&r1>=r)l=l1,r=r1;
		else hb=0;
		if(hb)res=countans(l,r);
		else res=countans(l,r)+countans(l1,r1);
		printf("%lld\n",res);
	}
	return 0;
}

这个区间合并的代码有点笨,引用一下大佬 jwMM 的方法

cin>>l1>>r1>>l2>>r2;
if(l2>r1||l1>r2)sum=get_num(l1,r1)+get_num(l2,r2);
else
{
    l=min(l1,l2);
    r=max(r1,r2);
    sum=get_num(l,r);
}

Problem G: 高木同学的因子 (1669) [27/38]

Tips

这个题其实已经见过了,当时也针对耗时优化过
再原来的基础上简单的改动了一下代码,可以说方法一点都没变
详细思路见原文章:NEFU 大一寒假训练五(GCD&&快速幂)2020.01.04
这不是巧了吗?当时也是 Problem G,题号都没变!

Code

#include <bits/stdc++.h>
using namespace std;

int main()
{
	long long yz[10000],yp=0,yznum;
	long long x,y,k,ans=0;
	scanf("%lld %lld",&x,&y);
	k=__gcd(x,y);
	if(k==1)
	{
		cout<<1;
		return 0;
	}
	for(int i=2;i<=k;i++)
	{
		if(k%i==0)
		{
			yz[yp++]=i;
			k/=i;
			i=1;
			if(k==1)break;
			continue;
		}
	}
	ans=1;
	yznum=1;
	for(int i=1;i<yp;i++)
	{
		if(yz[i]==yz[i-1])
		{
			yznum++;
		}
		else
		{
			ans*=yznum+1;
			yznum=1;
		}
	}
	ans*=yznum+1;
	printf("%lld",ans);
	return 0;
}

Problem H: 知否知否,应是绿肥红瘦 (1704) [48/102]

Tips

这个大概可能好像差不多不筛也行(数据非常特别十分水,1e14吓人用的)
(我不会告诉你我手滑不小心写了个传统判断法就过了)
我难以置信的又交了一次,还真是!
在这里插入图片描述
▲ 我猜以后一定会强化数据的,除非他们不知道,嘿嘿嘿 ╰( ̄ω ̄o)

Code

#include <bits/stdc++.h>
using namespace std;

int isss(long long n)
{
	for(long long i=2;i<=sqrt(n);i++)
	{
		if(n%i==0)return 0;
	}
	return 1;
}

int main()
{
	long long x,y,z;
	int n;
	scanf("%d",&n);
	while(~scanf("%lld %lld %lld",&x,&y,&z))
	{
		if(isss(x+y-z))printf("yes\n");
		else printf("no\n");
	}
	return 0;
}

Problem I: 素数线性筛 (2113) [21/65]

Tips

不算太难,把洛谷上的代码粘过来改一下范围就可以了。
可以读完范围以后筛出范围内的数,不用输入前就筛好。

Code

#include <stdio.h>
#include <string.h>

#define MAXN 40000001
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime(int area)
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
    	if(prime[i])primeList[num++]=i;
    	if(num>=area)break;
    	for(int j=0;j<num&&i*primeList[j]<MAXN;j++)
		{
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

int main()
{
	int n,q,k;
	scanf("%d %d",&n,&q);
	getPrime(n);
	while(q--)
	{
		scanf("%d",&k);
		printf("%d\n",primeList[k-1]);
	}
	return 0;
}

Problem J: 五十弦翻塞外声 (1262) [4/20]

Tips

哎呀,真是好诗啊!ヾ(≧▽≦*)o

醉里挑灯看剑,梦回吹角连营。八百里分麾下炙,五十弦翻塞外声,沙场秋点兵。
马作的卢飞快,弓如霹雳弦惊。了却君王天下事,赢得生前身后名。可怜白发生!

咳咳。。。走错片场了

这个题数据范围 1e9,理论上素数筛到 31623 就差不多了,
保险起见多筛一点,反正这筛子是是 O(n) 的,不差这点了。
然后将这个数不断用素数分解,找出它的所有素因子。
样例估计故意不想让你考虑全面,这里另外举个栗子吧:
24 可以拆成 2 2 2 3
其中因子 2 可以取 0,1,2,3 个,3 可以取 0,1 个
那么因子和可以表示为 (20+21+22+23) x (30+31)

代码中将加和的每个素因子放入栈中,最后乘到一起。
这个栈用数组模拟,队列之类的代替也可以,开心就好 (o゚v゚)ノ

更新说明:
2020.02.14 增强数据后只需将 MAXN 改为 1e6 以上即可。

Code

#include <bits/stdc++.h>
using namespace std;

#define MAXN 50000
char prime[MAXN];
int primeList[MAXN],num=0;
void getPrime()
{
    memset(prime,1,sizeof(prime));
    prime[0]=prime[1]=0;
    for(int i=2;i<MAXN;i++)
    {
        if(prime[i])primeList[num++]=i;
        for (int j=0;j<num&&i*primeList[j]<MAXN;j++)
        {
            prime[i*primeList[j]]=0;
            if(i%primeList[j]==0)break;
        }
    }
}

int main()
{
	stack<long long>s;
	long long yz[10000],yp=0,yznum,sum=0;
	getPrime();
	long long n;
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld",&n);
		if(n==1)
		{
			printf("1\n");
			continue;
		}
		yp=0;
		for(int i=0;i<num&&primeList[i]<=sqrt(n);i++)
		{
			if(n%primeList[i]==0)
			{
				yz[yp++]=primeList[i];
				n/=primeList[i];
				i=-1;
				if(n==1)break;
				continue;
			}
		}
		if(n!=1)yz[yp++]=n;
		yznum=1;
		for(int i=1;i<yp;i++)
		{
			if(yz[i]==yz[i-1])yznum++;
			else
			{
				sum=0;
				for(int j=0;j<=yznum;j++)sum+=pow(yz[i-1],j);
				s.push(sum);
				yznum=1;
			}
		}
		sum=0;
		for(int i=0;i<=yznum;i++)sum+=pow(yz[yp-1],i);
		s.push(sum);
		sum=1;
		while(!s.empty())
		{
			sum*=s.top();
			s.pop();
		}
		printf("%lld\n",sum);
	}
	return 0;
}
发布了62 篇原创文章 · 获赞 202 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/csg999/article/details/104311370