比赛链接: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;
}