时隔两年再次VP一场Codeforces Div. 2,一万八参赛,赛时(VP)排名71(第一次进前一百竟然是时隔两年的VP)
A. Zhan's Blender
简单题。
#include<bits/stdc++.h>
using namespace std;
int T, n, x, y;
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d%d", &n, &x, &y);
int t = min(x, y);
printf("%d\n", (n + t - 1) / t);
}
return 0;
}
B. Battle for Survive
由于 ai 可以为负数,因此问题答案可以看成:求序列中每个数进行加减得到最大的数是多少?
由于所有数初始全为正,因此我们想让尽可能多,尽可能大的数加进答案,而不是进行减法。
由于顺序,倒数第二个数一定是被减去的,而其他数可以先与倒数第二个数打架,然后由倒数第二个数传导到倒数第一个数,变成正的。
因此答案为所有数的和减去倒数第二个数的两倍
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int T, n, a[N];
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
long long sum = 0;
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
sum += a[i];
}
sum -= a[n - 1] * 2;
printf("%lld\n", sum);
}
return 0;
}
C. Password Cracking
第一次询问是否有 1 ,没有则为全 0 ,否则初始子串为 "1" 。
后面每次分别往字串后面添加 '0' 和 '1' ,询问两次,若有相同的,则子串长度加 1。
若往后加 '0' 和 '1' 都没有相同的,代表当前串是原字符串的后缀。
这时尝试往前加 '1' ,相同则加 '1' ,不同则加 '0'。
可以在 2n 次范围内询问出答案。
#include<bits/stdc++.h>
using namespace std;
int T, n, f;
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
cout << "? 1" << '\n';
cout.flush();
scanf("%d", &f);
if(f == 0) {
string ans = "";
for(int i = 1; i <= n; i ++)
ans += "0";
cout << "! " << ans << '\n';
cout.flush();
continue;
}
string ans = "1"; bool flag = 0;
for(int i = 2; i <= n; i ++) {
if(!flag) {
cout << "? " << ans + "1" << '\n';
cout.flush();
scanf("%d", &f);
if(f == 1) ans += "1";
else {
cout << "? " << ans + "0" << '\n';
cout.flush();
scanf("%d", &f);
if(f == 1) ans += "0";
else {
cout << "? " << "1" + ans << '\n';
cout.flush();
scanf("%d", &f);
if(f == 1) ans = "1" + ans;
else ans = "0" + ans;
flag = 1;
}
}
}
else {
cout << "? " << "1" + ans << '\n';
cout.flush();
scanf("%d", &f);
if(f == 1) ans = "1" + ans;
else ans = "0" + ans;
}
}
cout << "! " << ans << '\n';
cout.flush();
}
return 0;
}
D. Minimize the Difference
一个显而易见的结论:最后的答案序列一定是单调不减的。否则可以把前面大的数填补到后面小的数,不会使答案更劣,有可能使答案更优。
因此维护一个平均数单调栈,每次新加入一个数时,若栈顶元素平均数较大,则弹出并合并,直到栈顶元素平均数比当前平均数小,将新段入栈。
最后用最后一个数减去第一个数即为答案。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int T, n;
struct Node {
long long s, sz;
}stk[N * 2];
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
int top = 0;
for(int i = 1; i <= n; i ++) {
long long x;
scanf("%lld", &x);
long long sum = x, size = 1;
while(top && stk[top].s * size >= sum * stk[top].sz) {
sum += stk[top].s;
size += stk[top].sz;
top --;
}
if(sum % size == 0) stk[++ top] = {sum, size};
else {
int t = sum % size;
stk[++ top] = {sum / size * (size - t), size - t};
stk[++ top] = {(sum / size + 1) * t, t};
}
}
printf("%lld\n", stk[top].s / stk[top].sz - stk[1].s / stk[1].sz);
}
return 0;
}
E. Prefix GCD
设 g 为序列的最大公约数,我们将对每个元素进除以 g ,最后,只需将结果乘以 g 即可。
然后,考虑贪心算法。我们将从一个初始为空的数组开始并添加到数组的末尾,使用已有的数组最小化 GCD 的元素。可以观察到,GCD 将在最多 10 次迭代中达到 1 。之后,可以以任何顺序添加剩余的元素。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n, a[N];
bool vis[N];
int gcd(int a, int b) {
if(!b) return a;
return gcd(b, a % b);
}
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]), vis[i] = false;
sort(a + 1, a + n + 1);
long long ans = a[1];
int t = 0, gd = a[1], cnt = 1;
while(t != gd) {
t = gd;
int id = 0;
for(int i = 2; i <= n; i ++) {
if(vis[i]) continue;
if(gcd(t, a[i]) < gd)
gd = gcd(t, a[i]), id = i;
}
if(id) vis[id] = true;
ans += gd;
cnt ++;
}
ans += 1ll * gd * (n - cnt);
printf("%lld\n", ans);
}
return 0;
}