目录
A. Plus or Minus(签到)
题意
判断
a
+
b
=
c
a + b = c
a+b=c 是否成立,成立输出 +
,否则输出 -
.
思路
签到
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
void solve()
{
int a, b, c;
cin >> a >> b >> c;
if (a + b == c) cout << "+" << endl;
else cout << "-" << endl;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
B. Grab the Candies(贪心)
题意
给定一个长度为 n n n 的数组,A 和 B 依次轮流拿数,A 拿偶数,B 拿奇数,问能否保证任何时候 A 手中的数的总和都大于 B 。
思路
贪心。
偶数全给 A,奇数全给 B,最后比较大小,要求 suma > sumb
。
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
void solve()
{
int n;
cin >> n;
int suma = 0, sumb = 0;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
if (x % 2 == 0){
suma += x;
}
else {
sumb += x;
}
}
if (suma <= sumb)
cout << "NO" << endl;
else cout << "YES" << endl;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
C. Find and Replace(找规律)
题意
给定一个长度为
n
n
n 的仅包含小写字母的字符串,每次操作可以将每种字符变成 0
或 1
,操作不限次数。
问最终能否形成一个
01
01
01 串,使得没有连续的 0
或 1
,即
01
01
01 相互间隔排列,如:
010101
010101
010101 。
思路
如果两个相同字符之间包含 偶数 个其他字符,则不能形成 01 01 01 串,反之可以。
枚举 26 个字母的位置,依次判断两个相同字母之间的字符是否是奇数个即可。
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2010;
void solve()
{
int n;
cin >> n;
string s; cin >> s;
vector<vector<int>> p(30);
for (int i = 0; i < n; i++){
p[s[i] - 'a'].push_back(i);
}
int f = 0;
for (int i = 0; i < 26; i++){
if (p[i].empty()) continue;
int last = p[i][0];
for (auto pos : p[i]){
if ((pos - last - 1) % 2 == 0){
f = 1;
break;
}
last = pos;
}
}
if (f) cout << "NO" << endl;
else cout << "YES" << endl;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
D. Odd Queries(前缀和)
题意
给定一段长度为
n
n
n 的序列和
q
q
q 次询问,每次询问给定
l
l
l 、
r
r
r 和
k
k
k ,表示将区间 l,r
中的所有数都变成 k
,问每次变化后整个序列的和是多少。
思路
先预处理好前缀和,然后求出询问区间变化后的值,最后替换原区间得到整个序列和。
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
int n, q;
int a[N];
ll s[N];
void solve()
{
cin >> n >> q;
for (int i = 1; i <= n; i++){
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
while (q--)
{
int l, r, x;
cin >> l >> r >> x;
ll sum = (r - l + 1) * x;
if ((s[n] - s[r] + s[l - 1] + sum) % 2)
cout << "YES" << endl;
else cout << "NO" << endl;
}
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
E. Interview(二分)
题意
给定
n
n
n 个堆,第
i
i
i 个堆有
i
i
i 个 1
,也就是说第
i
i
i 个堆的大小为
i
i
i ,但是此时有一个 2
,使得某一个堆的大小为
i
+
1
i + 1
i+1 ,现在要你找出来这个堆是哪一个。
思路
交互题,根据每次 “?” 后猜测的区间给出相应的区间和,直到猜到目标堆,输出 “!” 和目标堆的序号。
因为数据不超过 2 ⋅ 1 0 5 2 · 10^5 2⋅105 ,最多需要 ⌈ l o g 2 ( 2 ⋅ 1 0 5 ) ⌉ = 18 ⌈log2(2⋅10^5)⌉=18 ⌈log2(2⋅105)⌉=18 次查询,远低于 30 30 30 ,所以可以使用二分来查找。
每次判断区间和是否等于给出的答案,如果等于则表示答案在另一段区间内,如果比给出的答案小 1 1 1 ,则在该区间内,继续对该区间二分。
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
int a[N];
ll s[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
int l = 1, r = n;
ll res = 0;
while (l < r)
{
int mid = (l + r) / 2;
cout << "? " << (mid - l + 1) << ' ';
for (int i = l; i <= mid; i++)
cout << i << ' ';
cout << endl << flush;
int x;
cin >> x;
if (x == s[mid] - s[l - 1])
l = mid + 1;
else r = mid;
}
cout << "! " << r << endl << flush;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
F. Bouncy Ball(模拟)
题意
给定一个 n × m
的地图,其中有一个小球,规定其移动方向为:
DR:右下↘
DL:左下↙
UR:右上↗
UL:左上↖
每次碰到边缘时会按照折射原理改变方向,如果是角落就按相反方向原路返回。
给出起点 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) 和终点 ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 和一个初始方向,问能否从起点到达终点。
如果能,输出改变方向的次数,若不能则输出 − 1 -1 −1 。
思路
模拟。
用 dx
和 dy
记录当前的方向,当碰到边缘壁时,判断是否在角落,改变方向和坐标并记录次数,直到到达终点。
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
#define y1 y
using namespace std;
const int N = 2e5 + 10;
int n, m;
int x1, y1, x2, y2;
void solve()
{
cin >> n >> m;
cin >> x1 >> y1 >> x2 >> y2;
string op;
cin >> op;
int dx = 1, dy = 1;
if (op[0] == 'U') dx = -1;
if (op[1] == 'L') dy = -1;
int s = 4 * n * m + 10;
int res = 0;
while (s--)
{
if (x1 == x2 && y1 == y2){
cout << res << endl;
return;
}
bool f1 = (x1 + dx < 1 || x1 + dx > n);
bool f2 = (y1 + dy < 1 || y1 + dy > m);
res += (f1 || f2);
//如果到了角落,则反方向
if (f1) dx *= -1;
if (f2) dy *= -1;
x1 += dx;
y1 += dy;
}
cout << -1 << endl;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
G2. Subsequence Addition (Hard Version)(前缀和)
题意
给定一个长度为 n n n 的目标序列,问能否由初始元素 1 1 1 构造而成。
1 1 1 可以用无限次,但最初序列中只有一个 1 1 1 ,每次可以合并若干个数得到一个新的数。
若能得到该序列输出 “YES” ,否则输出 “NO” 。
思路
首先目标序列中必须要有至少一个 1 1 1 ,否则不可能进行合成构造,所以先特判目标序列中是否有 1 1 1 。
可以先进行排序再判断第一个数。
然后由于目标序列中所有大于 1 1 1 的数都是由小的数合成而来的,所以如果所有比它小的数加起来都不能大于或等于它,那么这个数就无法得到,即无法构造出来。
所以我们可以使用 前缀和 来判断这个数能否被构造出来,只要这个数前面数的前缀和小于它则一定不能。
代码
#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
int n;
int a[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
sort(a + 1, a + 1 + n);
if (a[1] != 1){
cout << "NO" << endl;
return;
}
ll sum = a[1];
for (int i = 2; i <= n; i++){
if (sum < a[i]){
cout << "NO" << endl;
return;
}
sum += a[i];
}
cout << "YES" << endl;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}