比赛链接:https://ac.nowcoder.com/acm/contest/43898
作为大二的同学,第一次参加江西省赛,运气还不错,最后做出三题获得了二等奖,感谢我的队友在赛时的一起努力,还有志愿者为我们的无私奉献,特别感谢C老师为我们的操劳,周日一早就来机房为我们做准备,还有准备的午饭非常好吃,竟然有四个菜(我平时都没这么丰盛!!!
可惜的是这次的举办方为江西省教育厅,不是ICPC组委会,不然就是银牌爷了哈哈。
L Thunder God ’s Hammer
签到。
开赛时一刷新网页,竟然有人几秒钟就过题,于是我直接打开L,题都没看完就开始#include <iostream>
,2min就过了哈哈,交完发现28名,实在是手速题。
最后省赛榜单公布,如果三分钟之内过了L,竟然可以拿一个三等奖(难道是因为江西省太菜了x
#include <iostream>
using namespace std;
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int a, b, c, d;
cin >> a >> b >> c >> d;
cout << a * b * c * d << endl;
}
return 0;
}
A Special Adjustment Method
题意:给三个非负数x,y,z
,可以做n次操作(n可以等于0),每次操作k可以使一个数+2,两个数-1(操作过程中数要非负),求$x2+y2+z^2$的最大值。
WA了六发的神题,导致我们罚时爆炸,我们一开始发现要让小的数尽量小,大的数尽量大,然后直接排序后把最小的数减小为0,最大的数增加很多个2,交了一发WA。
然后X和Y同学灵机一动,发现最小的数如果先加大一点,加大到和中间的数差不多大,再一起减小,这样最大的数会更大,于是写了一发模拟,又WA。
然后X同学又发现,只要两个小的数相差是三的倍数,那么就可以一起减到0,如果不是就会减成01,写了一发又WA,百思不得其解。
于是我们换题写B。。。
B写不出来,又换回A。
通过智慧样例,我们把条件改成if (a[1] == a[2] || ((a[1] - a[0]) % 3 == 0))
,结果又WA!
最后我发现可以if (((a[2] - a[1]) % 3 == 0) || ((a[1] - a[0]) % 3 == 0))
,然后后WA,最后我乱打了一个测试样例1 9 10
发现如果最小的和最大的相差3的倍数,也可以达成0 0
的结果,提交了一发终于AC!
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
int t;
cin >> t;
for (int i = 1; i <= t; i++) {
long long a[3];
cin >> a[0] >> a[1] >> a[2];
sort(a, a + 3);
if (((a[2] - a[0]) % 3 == 0) || ((a[2] - a[1]) % 3 == 0) ||
((a[1] - a[0]) % 3 == 0))
cout << (a[0] + a[1] + a[2]) * (a[0] + a[1] + a[2]) << endl;
else
cout << (a[0] + a[1] + a[2] - 1) * (a[0] + a[1] + a[2] - 1) + 1
<< endl;
}
return 0;
}
B 01string
题意:给一个序列s0由0和1组成。s1比s0少最后一位,并且其中第i位上的值为s0上的max(i,i+1),以此类推s2,s3...。给序列s0和数字k,求s0,s1,s2....sk的所有序列中1的总个数。
很奇怪的题目,我的队友写了两个小时等差数列还没过,他们给我解释我也听不懂,于是我去吃中饭。我还以为这个思路有问题,没想到最后题解出来,和他们的思路适合是相匹配的,但是我还是看不懂呜呜。
说说我的AC思路,对于序列中的每一个字母,我们用ap(appeareance)来表示它第一次出现1的时间,如果在是s0时就是1了,那我就就记录为0,如果在s1的时候出现1,那就记录为1。再用dis(disappear)来记录这个数字消失的时间,因为从s0-s1会丢失最后一个字母,s1-s2又会丢失最后一个字母,如果这个数字是在s1的时候消失的(它是最后一个字母),我们就记录为0。
对于每一个序列的字母,要求他们的ap,我们只需要记录它距离它右边最近的一个1的距离即可(因为它只能被右边的1更新)。要求他们的dis,我们只需要记录它离字符串末尾的距离即可。
求出每个序列的ap和dis,要求k时1的总数,就只需要遍历一遍序列,如果出现(ap)在k之前,那就加上dis-ap
,如果k在ap后,那就不用管,这样只需要遍历一遍序列就可以求解出答案了。
#include <algorithm>
#include <cstring>
#include <iostream>
#define int long long
using namespace std;
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
int t;
cin >> t;
for (int i = 0; i < t; i++) {
int n, k;
cin >> n >> k;
string a;
cin >> a;
int last1 = 0x3f3f3f3f;
int ap, dis;
int cnt = 0;
for (int j = n - 1; j >= 0; j--) {
if (a[j] == '0')
ap = last1 - j;
else
last1 = j, ap = 0;
dis = n - j;
// cout << ap << ' ' << dis << endl;
if (ap < k) {
if (dis > k)
cnt += k - ap + 1;
else
cnt += dis - ap;
}
if (ap == k) cnt++;
// cout << "cnt=" << cnt << endl;
}
cout << cnt << endl;
}
return 0;
}
C Graphic Game
给一个 2×𝑛 个点,𝑚 条边,没有重边和自环的无向图,每次可以选择两个度数相等的点删除,求删完所有点的方案
赛时是手写了一个find函数,用来找两个最大的点,
度数的可能取值有 1,…,𝑚,而点数 2×𝑛>𝑚 个,即一个无向图最少存在两个点的度数相等,删除两个点后仍然是一个无向图,即该问题一定有解,先存下每个度数对应的节点,考虑从最大的度数开始删除两个数,用 𝑠𝑒𝑡 维护即可,为什么这样一定能删完?即现在要证明不存在这样一种情况:删除当前度数为 𝑡 的两个数后,所有度数 𝑥<=𝑡 的节点个数都不超过 1,但是可能存在度数 𝑥>𝑡 的节点个数超过 1,可以发现这样的情况很难构造出来,因为对于一个度数 𝑥>𝑡 的节点来说,说明会有 𝑥 条边由该节点连向其他节点,假设如果其他点的度数都 ≤𝑡,而有 𝑥 个这样的点,说明至少存在两个度数相等的点,假设不成立,故与该节点连的点中至少存在一个点的度数 >𝑡,即我们的那种要证明的不存在的情况说明:度数大的点至少也会跟度数大的点连边,且所有其他度数小的节点都不存在相等度数的节点,这样的情况更加难以构造。
source:https://www.cnblogs.com/zyyun/p/16819062.html
#include <bits/stdc++.h>
using namespace std;
// 快读模版
template <typename T>
void inline read(T &x) {
int f = 1;
x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-') f = -1;
s = getchar();
}
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N = 2e6 + 10, M = 1e6 + 10;
int h[N], e[N], ne[N], idx;
int n, m;
int d[N];
set<int> deg[M]; // 存度数
void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
int main() {
// 点 边
read(n), read(m);
memset(h, -1, sizeof h);
// 存边
for (int i = 1; i <= m; i++) {
int a, b;
read(a), read(b);
add(a, b), add(b, a);
d[a]++, d[b]++;
}
// 把点的度数插入set当中
for (int i = 1; i <= 2 * n; i++) deg[d[i]].insert(i);
// 循环 m 次,因为一个点最多有 m 度
int t = m;
while (t >= 0) {
// 如果这个度数中,点的个数大于2
if (deg[t].size() >= 2) {
// 把前两个点取出来
int x = *deg[t].begin();
deg[t].erase(deg[t].begin());
int y = *deg[t].begin();
deg[t].erase(deg[t].begin());
// 输出
printf("%d %d\n", x, y);
// 再删除与这个点相连的边
d[x] = d[y] = 0;
for (int i = h[x]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > 0) {
// 度数-1
deg[d[j]].erase(j);
d[j]--;
deg[d[j]].insert(j);
}
}
for (int i = h[y]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] > 0) {
deg[d[j]].erase(j);
d[j]--;
deg[d[j]].insert(j);
}
}
} else {
// 如果这个度数中,只有0或1个点
// 那么这个度就没有遍历的必要
t--;
}
}
return 0;
}