1001题目链接
题意:给定数x,求包含x的区间和为素数的最小区间长度
分析:
显然 r > 0,r ≥ l,如果 l ≤ 0,此时 [l,r] 区间和 = [−l + 1,r] 的区间和,且 r ≥ −l + 1 > 0。
故对于任意一个区间,都可以找到一个对应的 r ≥ l > 0 的区间与之对应,区间和相同。对于下式:
∑
i
=
l
r
i
=
(
l
+
r
)
(
r
−
l
+
1
)
2
\sum_{i=l}^ri=\frac{(l+r)(r-l+1)}{2}
∑i=lri=2(l+r)(r−l+1)
当 l > 0 时,若 r − l ≥ 2,则 l + r 2 \frac{l+r}{2} 2l+r和 r − l + 1 2 \frac{r-l+1}{2} 2r−l+1之中必有一个是大于 1 的整数。区间和必然能被拆分成两个因数的乘积。所以这样的满足和为素数的区间长度必然不长于 2。
预处理出比 [2,2 × 10 7 ] 略大范围内的质数,埃氏筛或者线筛均可。
如果 x ≤ 0,则候选答案为:
- 最小的 y(y ≥ 1 − x),满足 y 是质数,区间为 [−y + 1,y]。
- 最小的 z(z ≥ 2 − x),满足 2z − 1 是质数,区间为 [−z + 2,z]。
如果 x > 0,则候选答案为:
- [x,x]。
- [x,x + 1]。
- [x − 1,x]。
- 最小的 y(y ≥ x),满足 y 是质数,区间为 [−y + 1,y]。
- 最小的 z(z ≥ x),满足 2z − 1 是质数,区间为 [−z + 2,z]。
二分查找即可,时间复杂度为 O(T log(|x|) + |x|)。
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<string>
#include<queue>
#define maxn 20000010
using namespace std;
int prime[maxn];
int visit[maxn];
void Prime() {
memset(visit, 0,sizeof(visit));
memset(prime, 0,sizeof(prime));
visit[0] = 1;
visit[1] = 1;
for (int i = 2; i <= maxn; i++) {
if (!visit[i]) {
prime[++prime[0]] = i;
}
for (int j = 1; j <= prime[0] && i * prime[j] <= maxn; j++) {
visit[i * prime[j]] = 1;
if (i % prime[j] == 0) {
break;
}
}
}
}
int main()
{
int t;
Prime();
scanf("%d", &t);
while (t--)
{
int x;
scanf("%d", &x);
int minindex = maxn;
if (x <= 0)
{
int i;
for (i = 1 - x; visit[i]; ++i);
minindex = min(minindex, 2 * i);
for (i = 2 - x; visit[2 * i - 1]; ++i);
minindex = min(minindex, 2*i-1);
printf("%d\n", minindex);
}
else
{
int i = 0;
if (!visit[x])
printf("1\n");
else
{
if (!visit[2 * x + 1] || !visit[2 * x - 1])
printf("2\n");
else
{
for (i = x; visit[i]; ++i);
minindex = min(minindex, 2*i);
for (i = x; visit[2 * i - 1]; ++i);
minindex = min(minindex, 2 * i - 1);
printf("%d\n", minindex);
}
}
}
}
return 0;
}
1005 题目链接
题目大意:
给定整数 1 , 2 , ⋯ , n 把这些数字分成m个不相交的集合,使第j个集合的中位数为bj。确定这是否可能。
注:这题中,偶数个数的集合的中位数取中间两个数中较小的那个。
题目分析:
题目分析:
显然
b
1
b_1
b1,
b
2
b_2
b2,··· ,
b
m
b_m
bm 这 m 个数要放在 m 个不同的集合中,剩下的 n − m 个数字要放到这 m 个集合里且不影响每个集合的中位数。
使用一个例子以方便说明:假设 n = 6,m = 2,
b
1
b_1
b1 = 3,
b
2
b_2
b2 = 5,那么 1,2,··· ,n 这些数会被b分成 1,2、4、6 这三段,且任意两段中的任意一对数字可以配对消掉。
所以最后剩下的所有数字一定是同一段内的。
因此讨论两种情况:
- 如果长度最大的段的数字个数不大于其它段的数字个数之和,那么最终要么全部消掉,要么剩下一个,且剩下的这个数可以在任何一段内。
如果会剩下,不妨将最后一段的数字剩下一个,此时再把最后一段的数字放到中位数最小的集合中即可满足题意,所以答案为 YES。 - 如果长度最大的段的数字个数大于其它段的数字个数之和,那么最终剩下的所有数字都在最大的这段内。
设中位数小于这一段的最小值的集合的个数为 x,容易发现当且仅当 x 不小于这一段剩下的数字个数时有解。
时间复杂度 O ( n ) O(n) O(n)。
AC代码:
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
#include<assert.h>
using namespace std;
typedef pair<int, int> Pair;
#define maxn 100005
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
vector<Pair> size;
int n, m, b[maxn];
int flag[maxn] = { 0 };
cin >> n >> m;
for (int i = 1; i <=m; ++i)
cin >> b[i],flag[b[i]] = 1;
flag[n + 1] = 1;
int len = 0, num = 0;
for (int i = 1; i <=n+1; ++i)
{
if (flag[i])
{
if (len)
size.push_back(Pair(len, num));
len = 0;
num++;
}
else
len++;
}
int sum = 0;
for (auto s : size)
sum += s.first;
if (size.empty())
cout << "YES" << endl;
else
{
sort(size.begin(), size.end());
if (size.back().first <=sum - size.back().first+size.back().second)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
}
return 0;
}
1008题目链接
分析
考虑范围为 k 的狙击手,可以攻击的范围是一个正方体。那我们不妨重新陈述一下题面,称狙击手从 ( x , y , z ) (x,y,z) (x,y,z) 可以攻击的范围是所有满足 x ≤ x ′ ≤ x + 2 k x≤x′≤ x + 2k x≤x′≤x+2k, y ≤ y ′ ≤ y + 2 k y ≤ y ′ ≤ y + 2k y≤y′≤y+2k, z ≤ z ′ ≤ z + 2 k z ≤ z ′ ≤ z + 2k z≤z′≤z+2k的 ( x ′ , y ′ , z ′ ) (x ′ ,y ′ ,z ′ ) (x′,y′,z′)。
那么我们就会发现:如果我们的狙击手的某一维度的坐标小于剩余点的那一维度的最小坐标,我们就应该把狙击手移动的那一维度的最小坐标,因为这样只会让我们能狙击的目标增
多。
在我们三个维度都是那一维度的最小坐标时,狙击手就必须将三个维度中某一维度最小坐标的所有敌军都消灭,才能继续移动。这时我们可以贪心的选取所需 k 最小的敌军消灭。
复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
AC代码
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<assert.h>
#include<string>
#include<queue>
#include<algorithm>
#include<set>
using namespace std;
typedef pair<int, int> P;
#define maxn 500005
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
set<P>sets[3];
vector<vector<int> >pos(3,vector<int>(n+1,0));
for (int i = 0; i < n; ++i)
{
cin >> pos[0][i] >> pos[1][i] >> pos[2][i];
sets[0].insert(make_pair(pos[0][i], i));
sets[1].insert(make_pair(pos[1][i], i));
sets[2].insert(make_pair(pos[2][i], i));
}
int ans = 0;
while (sets[0].size())
{
P x = *sets[0].begin(), y = *sets[1].begin(), z = *sets[2].begin();
vector<P>k;
for (int i = 0; i < 3; ++i)
{
P cur =*sets[i].begin();
k.push_back(make_pair(max(pos[0][cur.second] - x.first, max(pos[1][cur.second] - y.first, pos[2][cur.second] - z.first)), cur.second));
}
sort(k.begin(), k.end());
ans = max(ans, k[0].first);
for (int i = 0; i < 3; ++i)
sets[i].erase(P(pos[i][k[0].second], k[0].second));
}
cout << ( ans + 1 ) / 2 << endl;
}
return 0;
}