henuoj 1166 TonyStark with Prime(Hard Edition) 哥德巴赫猜想

题目链接

哥德巴赫猜想:任何一个大于2的偶数能拆成两个素数之和

正好可以用在本题

(题目描述中说的任何一个大于3的数能拆分乘两个素数和是错误的,举个例子,11,如果想拆分成两个数的和必须是一个奇数一个偶数,偶数素数只有2,奇数只能是9,显然不成立)

本题目中给定一个数字 n ,(n <= 1e12),求这个数字能否由四个素数组成,如果能,输出所有方案中的任何一个,如果不能输出3000.

首先,最小的素数是2,所以2 * 4 = 8,要想组成最小是8,小于8无法完成输出3000,对于大于8的都可以。

原因:如果大于8是一个偶数,拆分成两个偶数,利用哥德巴赫猜想可以明显看出可以,如果是奇数,先拆分出一个2一个3,剩下的又是一个偶数拆分成两个素数,很明显可以。

我们怎么求满足条件的数字呢?

首先预处理一个区间内的所有素数(埃式筛),对输入的数字 n 进行判断,如果小于8就输出3000,否则判断奇偶性,如果是偶数,就把偶数拆分成为两个偶数 t1 , t2 ,对每一个偶数,循环遍历刚刚区间内的全部数字,如果有一个 数满足 (t1 - i)是素数,则记录下来跳出循环,另外一个偶数同样操作,如果是奇数,首先得到两个数2 和 3,然后 n -= 5,回到刚刚的一个步骤求解即可。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
ll n;
ll v[1000000];
void pre_work(){
	v[0] = -1;
	v[1] = -1;
	for (int i = 2; i < 1000000; i++){
		if(v[i] != 0)continue;
		v[i] = 1;
		for (int j = 2; j * i < 1000000; j++){
			v[i * j] = -1;
		}
	}
}
int is(ll num){
	if(num == 1)return 0;
	if(num == 0)return 0;
	if(num == 2)return 1;
	ll sq = sqrt(num);
	for (ll i = 2; i <= sq; i++){
		if(num % i == 0)return 0;
	}
	return 1;
}
int main()
{
	vector<ll> prime;
	pre_work();
	for (int i = 2; i < 1000000; i++)if(v[i] == 1)prime.push_back(i);
	while (cin >> n){
		if(n < 8){
			cout << "3000" << endl;
			continue;
		}
		if(n & 1){
			ll a, b, c, d;
			a = 2;
			b = 3;
			n -= 5;
			for (ll i : prime){
				if(is(n - i)){
					c = i;
					d = n - i;
					break;
				}
			}
			printf("%lld %lld %lld %lld\n", a, b, c, d);
		}
		else{
			ll a, b, c, d;
			ll t1 = n / 2;
			ll t2 = t1;
			if(t1 & 1){
				t1++;
				t2--;
			}
			for (ll i : prime){
				if(is(t1 - i)){
					a = i;
					b = t1 - i;
					break;
				}
			}
			for (ll i : prime){
				if(is(t2 - i)){
					c = i;
					d = t2 - i;
					break;
				}
			}
			printf("%lld %lld %lld %lld\n", a, b, c, d);
		}
	}
	return 0;
}

 

posted @ 2019-08-06 23:15  correct  阅读(134)  评论(0)    收藏  举报