比赛链接:第十七届中国计量大学程序设计竞赛
目录
B - Broken Pad
两种情况:
- 原字符串从左往右翻转;
- 先单击空白处,再从左往右翻转。
结果取两者中次数较少的。
#include <iostream>
#include <string>
#include <queue>
using namespace std;
#define io_speed_up ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
string a, b;
int t, cnt1, cnt2, cnt, len;
queue <int> q1, q2;
int main() {
io_speed_up;
cin >> t;
while (t--) {
cin >> a >> b;
while (!q1.empty()) q1.pop();
while (!q2.empty()) q2.pop();
cnt1 = cnt = 0, len = a.length();
for (int i = 0; i < len; ++i) {
int t1 = a[i] - '0', t2 = b[i] - '0';
if (cnt && t1 ^ 1 != t2) {
q1.push(i + 1);
cnt ^= 1;
cnt1++;
} else if (!cnt && t1 != t2) {
q1.push(i + 1);
cnt ^= 1;
cnt1++;
}
}
cnt2 = 1, cnt = 0;
q2.push(0);
for (int i = 0; i < len; ++i) {
int t2 = b[i] - '0';
if (cnt && !t2) {
q2.push(i + 1);
cnt ^= 1;
cnt2++;
} else if (!cnt && t2) {
q2.push(i + 1);
cnt ^= 1;
cnt2++;
}
}
if (cnt1 <= cnt2) {
while (!q1.empty()) {
cout << q1.front() << ' ';
q1.pop();
}
} else {
while (!q2.empty()) {
cout << q2.front() << ' ';
q2.pop();
}
}
cout << endl;
}
return 0;
}
C - Cook Steak
一道简单的模拟题,按顺序计算时间即可。
#include <iostream>
#include <vector>
using namespace std;
#define ll long long
int t, n;
int main() {
cin >> t;
while (t--) {
cin >> n;
vector <pair<int, int> > vec;
for (int i = 1; i <= n; ++i) {
int l, r;
cin >> l >> r;
vec.push_back(make_pair(l, r));
}
int now = 0, p = 0;
ll time = 0;
while (p < n) {
int v = 1;
if (now < vec[p].first) {
v = vec[p].first - now;
now = vec[p].first;
}
else if (now > vec[p].second) {
v = now - vec[p].second;
now = vec[p].second;
}
else {
p++;
}
time += v;
}
cout << time << endl;
}
return 0;
}
D - Dessert Time
首先我们将出现的数字排序,统计出现次数,这里选用map存储。
可以分类成以下三种情况:
- 从大到小看,第一个出现奇数次的数是最小的数:
如果我们第一步拿最小的数,那么之后对手只需要按顺序拿即可让我们拿最后一个数;如果我们第一步拿其他数,那么对手只需模仿我们的操作,直到拿掉最后一个数让我们兜底;因此,这是必败态; - 从大到小看,第一个出现奇数次的数不是最小的数:
此时我们只要第一步拿这个奇数次的数就是必胜,当我们拿了该数以后,它及它之后的数字全都是偶数次,我们只需要模仿对手的操作即可拿到最后一个数使对手兜底; - 所有数都是偶数次:
此时我们先手取最小的数必胜,若对手模仿我们的操作,他会拿到最后一个数,若他不模仿我们的操作而先拿一个较大的数,那么我们模仿他的操作,就能拿到最后一个数使对手兜底。
#include <iostream>
#include <map>
using namespace std;
int t, n, x, ans;
map <int, int> mat;
int main() {
cin >> t;
while (t--) {
mat.clear();
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> x;
mat[x]++;
}
ans = -1;
for (auto iter = (--mat.end()); ; --iter) {
if (iter->second & 1) {
ans = iter->first;
break;
}
if (iter == mat.begin()) {
break;
}
}
if (ans == -1) {
ans = mat.begin()->first;
} else if (ans == mat.begin()->first) {
ans = -1;
}
cout << ans << endl;
}
return 0;
}
E - Eat Grapes
首先考虑一般情况:
当一个节点连接大于1个葡萄时,先手的人总能通过策略留最后1个葡萄给对手,所以这题变成在求当遇到第一个拥有大于1个葡萄的节点时谁是先手;
当一个节点没有连接葡萄时,它只拥有上一个节点这1个葡萄,所以我们只要知道最后连续有多少个节点没有连接葡萄,就可以知道之后谁先手;
由于最后一个节点总留有一个额外的葡萄,所以偶数个连续的节点代表SYH赢。
特殊情况:
如果所有节点都没有连接葡萄,那么结果与上面的情况相反。
#include <iostream>
using namespace std;
int t, n, num, cnt;
int main() {
cin >> t;
while (t--) {
cin >> n;
cnt = 0;
for (int i = 1; i <= n; ++i) {
cin >> num;
if (num) {
cnt = 0;
} else {
cnt++;
}
}
if (cnt == n) {
if (cnt % 2) {
cout << "these are sweet grapes" << endl;
} else {
cout << "these are sour grapes" << endl;
}
} else if (cnt % 2) {
cout << "these are sour grapes" << endl;
} else {
cout << "these are sweet grapes" << endl;
}
}
return 0;
}
F - Flag Scramble Competition
签到题,数都能数出来。
#include <cstdio>
using namespace std;
int main() {
printf("e");
return 0;
}
H - Happy Time is Always Short
线段树的模板题,把求和改成取最大值即可。
#include <cstring>
#include <cstdio>
#include <iostream>
using namespace std;
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
struct node {
ll tree, left, right, lazy;
} a[400050];
ll t, n, m, add, ans, x, y;
ll read() {
char x = getchar(); ll ans = 0; int f = 1;
while (!isdigit(x)) {
if (x == '-') f = -1;
x = getchar();
}
while (isdigit(x)) {
ans = (ans << 3) + (ans << 1) + (x ^ 48);
x = getchar();
}
return ans * f;
}
void create(ll l, ll r, ll k) {
a[k].lazy = 0, a[k].left = l, a[k].right = r;
if (l == r) {
a[k].tree = read();
return;
}
ll mid = l + r >> 1;
create(l, mid, k * 2);
create(mid + 1, r, k * 2 + 1);
a[k].tree = max(a[k * 2].tree, a[k * 2 + 1].tree);
return;
}
void load(ll k) {
ll l = k * 2, r = k * 2 + 1;
a[l].lazy = 1;
a[r].lazy = 1;
a[l].tree = 0;
a[r].tree = 0;
a[k].lazy = 0;
return;
}
void change(ll k) {
if (a[k].left >= x && a[k].right <= y) {
a[k].tree = 0;
a[k].lazy = 1;
return;
}
if (a[k].lazy) load(k);
ll mid = a[k].left + a[k].right >> 1;
if (x <= mid) change(k * 2);
if (y >= mid + 1) change(k * 2 + 1);
a[k].tree = max(a[k * 2].tree, a[k * 2 + 1].tree);
return;
}
void inquery(ll k) {
if (a[k].left >= x && a[k].right <= y) {
ans = max(ans, a[k].tree);
return;
}
if (a[k].lazy) load(k);
ll mid = a[k].left + a[k].right >> 1;
if (x <= mid) inquery(k * 2);
if (y >= mid + 1) inquery(k * 2 + 1);
return;
}
int main() {
t = read();
while (t--) {
ms(a, 0);
n = read();
m = read();
create(1, n, 1);
while (m--) {
x = read(), y = read();
ans = 0;
change(1);
x = 1, y = n;
inquery(1);
printf("%lld
", ans);
}
}
return 0;
}
I - Isolated Pointset
根据说明就能看出来,只要大于等于三个点,就可以一直拼成一排等边三角形。
#include <iostream>
using namespace std;
int t, num;
int main() {
cin >> t;
while (t--) {
cin >> num;
if (num > 2) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
J - Jiufeng's Football Team
用种类并查集来区分两个团队,在球员之间建边,边权为训练时间。
给定一个时间,大于该时间的边所属的两个球员放在不同团队;当所有球员都分配完毕时,没有出现矛盾的情况,则该时间满足条件;若出现两个球员的训练时间大于给定时间,但根据之前球员的分配,这两个球员在同一团队,则产生矛盾,该时间不满足条件。
通过二分的方法来获取训练时间的最小值。
#include <iostream>
#include <vector>
using namespace std;
#define ll long long
#define MAXN 400010
int t, n, m, fa[MAXN], x, y, w;
struct Edge {
int to, w;
};
vector <Edge> edge[MAXN];
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool check(int x) {
for (int i = 1; i <= 2 * n; ++i) fa[i] = i;
for (int i = 1; i <= n; ++i) {
for (auto iter : edge[i]) {
if (iter.w <= x) continue;
int nxt = iter.to;
int x = find(i);
int y = find(nxt);
if (x == y) return false; // 在同一个团队
fa[find(x + n)] = find(y);
fa[find(y + n)] = find(x);
}
}
return true;
}
int main() {
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1; i <= n; ++i) edge[i].clear();
for (int i = 1; i <= m; ++i) {
cin >> x >> y >> w;
edge[x].push_back(Edge{y, w});
edge[y].push_back(Edge{x, w});
}
int l = 0, r = 1e9, ans = r;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid - 1;
ans = mid;
} else {
l = mid + 1;
}
}
cout << ans << endl;
}
return 0;
}
K - Known-Well Palindrome Date-Easy Version
按顺序从左往右判断:
- 先用string类的substr函数每次截取八个字符;
- 用algorithm头文件里的reverse函数翻转截取的字符串,并与原字符串判断回文;
- 判断八个字符中是否存在空格;
- 判断日期的合理性。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int n, len;
string s;
bool judge(int year) {
return (!(year % 400) || year % 100 && !(year % 4));
}
int main() {
while (getline(cin, s)) {
if (s == "#") break;
n = 0;
len = s.length();
for (int i = 0; i <= len - 8; ++i) {
// 判断回文
string s1 = s.substr(i, 8);
string s2 = s1;
reverse(s1.begin(), s1.end());
if (s1 == s2) {
// 去除空格
bool flag = false;
for (int j = i; j <= i + 7; ++j) {
if (s[j] > '9' || s[j] < '0') {
flag = true;
break;
}
}
if (flag) continue;
// 判断日期合理性
int yy = (((s[i] - '0') * 10 + s[i + 1] - '0') * 10 + s[i + 2] - '0') * 10 + s[i + 3] - '0';
int mm = (s[i + 4] - '0') * 10 + s[i + 5] - '0';
int dd = (s[i + 6] - '0') * 10 + s[i + 7] - '0';
if (mm == 1 || mm == 3 || mm == 5 || mm == 7 || mm == 8 || mm == 10 || mm == 12) {
if (dd <= 31 && dd > 0) {
n++;
}
} else if (mm == 4 || mm == 6 || mm == 9 || mm == 11) {
if (dd <= 30 && dd > 0) {
n++;
}
} else if (mm == 2) {
if (judge(yy)) {
if (dd <= 29 && dd > 0) {
n++;
}
} else {
if (dd <= 28 && dd > 0) {
n++;
}
}
}
}
}
cout << n << endl;
}
return 0;
}