2021杭电第六场 补题

比赛链接:Contest Problem List (hdu.edu.cn)

1>1001Yes, Prime Minister

样例:

输入:

10
-2
-1
0
1
2
3
4
5
6
7

 输出:

6
4
3
2
1
1
2
1
2
1

题目翻译:对于每组测试给出的x,你需要找到一个区间使得区间和为一个质数,同时使得区间的长度尽可能小,对于每个测试样例输出最短输出区间长度。

官方题解思路:

在这里插入图片描述

 对于一个正数而言,有五种可能的情况,①自己本身为一个质数,此时区间长度为1②自己与旁边的“邻居”之和为质数对应(x-1,x)以及(x,x+1)两种情况,③当前两种条件都不满足的时候,我们应该继续向后寻找,很容易可以发现三个及以上连续自然数的和一定是一个非质数,因此,我们要找的有两种情况,x之后本身是质数的数y,对应的长度为(-y+1,y),或者x之后两个相邻数的和为质数的情况(z-1,z)此时的长度为(-z+2,z)。

当x为负数的时候首先应该加到正区间-x(质数都是正数)从x+1向后寻找对应上面的情况③

首先我们对2e7以内的质数进行预处理,之后进行二分查找即可。

代码:

#include <bits/stdc++.h>
#define _ 0
#define ll long long
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 2e7 + 9;
ll n, m;
ll v[maxn];
vector <ll> p;
//欧拉筛进行筛选质数 
void xxs()
{
	for(ll i = 2; i <= maxn - 5; ++i)
	{
		if(!v[i]) p.push_back(i);
		for(int j = 0; j < p.size() && p[j] * i < maxn; ++j)
		{
			v[p[j] * i] = 1;
			if(i % p[j] == 0) break;
		}
	}
}

void work()
{
	scanf("%lld", &n);
	//n为0,1,-1的时候进行特判 
	if(!n) printf("3\n");
	else if(n == 1) printf("2\n");
	else if(n == -1) printf("4\n");
	else if(n > 1)
	{//大于1的时候 
		//假如n是质数  直接长度为1 
		if(!v[n]) cout << 1 << endl;
		//本身不是质数  但是与相邻的数之和是质数   长度为2 
		else if(!v[n*2+1] || !v[n*2-1]) cout << 2 << endl;
		else
		{	//从n向后查找最小质数  (单个质数) 
			int pos = upper_bound(p.begin(), p.end(), n) - p.begin();
			//长度为质数 位置*2(前面的加到负数都抵消掉) 
			ll ans = p[pos] * 2;
			//从n+n+1开始向后查找两位数和为质数的情况 (x+x+1) 
			int poss = lower_bound(p.begin(), p.end(), 2 * n + 3) - p.begin();
			//找到该质数对应的x对应的前一个数x1   区间长度为x1*2加上0的位置,加上两个质数 
			ll x1 = p[poss] / 2 - 1;
			//获取两种方案里面更优的 
			ans = min(ans, x1 * 2 + 1 + 2);
			printf("%lld\n", ans);
		}
	}
	else
	{	//加到正数  开始向后查找单个的质数 
		int pos = upper_bound(p.begin(),p.end(), abs(n)) - p.begin();
		ll ans = p[pos] * 2;
		//查找两个和为质数的情况 
		int poss = lower_bound(p.begin(), p.end(), abs(n) * 2 + 3) - p.begin();
		ll x1 = p[poss] / 2 - 1;
		ans = min(ans, x1 * 2 + 1 + 2);
		printf("%lld\n", ans);
	}
}

int main()
{
	xxs();
    int TT;cin>>TT;while(TT--)
    work();
    return ~~(0^_^0);
}

2>1005

样例:

输入:

3
4 4
2 4 3 1
4 3
1 3 4
4 3
2 3 4

输出:

YES
YES
NO

翻译:docriz 先生有 n 个不同的整数 1,2,⋯,n。他想把这些数字分成m个不相交的集合,这样第j个集合的中位数是bj。请帮助他确定是否可行。 注意:对于一个大小为k的集合,将其中的元素排序为c1,c2,⋯,ck,这个集合的中位数定义为c⌊(k+1)/2⌋(向下取整)。

解题思路:网上找到了一位大佬的dp (杭电第六场 (思维 ( 贪心 || dp_cosx_的博客-CSDN博客)用两个数组f[i],g[i]分别来存储最多和最少需要使用b[i]之后的多少位数通过递推关系得到最终答案。(具体看代码注释)

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
int n, m;
int b[maxn], f[maxn], g[maxn];
// f[i], g[i] 分别代表前i组最多和最少要用多少bi之后的数 
void work()
{
	cin >> n >> m;
	for(int i = 1; i <= m; ++i)	scanf("%d", &b[i]);
	sort(b+1,b+1+m);//从小到大进行排序
	//初始化 刚开树均为0
	
	for(int i = 1; i <= m; ++i) f[i] = g[i] = 0;
	for(int i = 1; i <= m; ++i)
	{	
		//最多要用多少b[i]之后的数  把前一个i-1之后需要使用的数也全部转化为i之后使用使i之后的数达到最大 
		f[i] = f[i-1] + b[i] - b[i-1];
		//前i-1个需要使用的数都尽量在b[i]到b[i-1]之间使用   b[i-1]到b[i]之间的数被消耗完的时候x为0 
		//x表示当尽量消耗 b[i-1]到b[i]之间的数字的时候 这个区间内最少能剩多少(下限为0) 
		int x = max(0, b[i] - b[i-1] - f[i-1] - 1);
		
		cout<<x<<endl; 
		// x表示b[i-1]到b[i]之间有多少没有消耗掉的数字  是需要在后面继续找的 
		// max部分表示的是b[i-1]前面的部分需要向后面借多少   这两部分加起来表示的是最少需要用多少 
		g[i] = x + max(0, g[i-1] - (b[i] - b[i-1] - 1));
	}
	//f[m]>=n-b[m]表示最多要使用的数字超出了n
	//g[m]<=n-b[m]表示最少要使用的数字没有超出n
	//这两种情况同时满足  那么就意味着一定存在一种情况使得满足题意 
	if(b[m] + f[m] >= n && b[m] + g[m] <= n) cout << "YES\n";
	else cout << "NO\n";
}

int main()
{
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值