一万一参赛,赛时排名 151
A. Meaning Mean
简单贪心题。显然,排在越后面的数,除以 2 的次数越少。
因此贪心地从小到大计算结果即为答案。
#include<bits/stdc++.h>
using namespace std;
const int N = 55;
int T, n, a[N];
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
int ans = a[1];
for(int i = 2; i <= n; i ++) {
ans = (ans + a[i]) / 2;
}
printf("%d\n", ans);
}
return 0;
}
B. Maximize Mex
简单模拟题。显然,若一个数在原序列中没有出现,那它只能由更小的数通过加法得到。
这个更小的数需要满足它本身是多出来的,且它和当前缺少的数相差 k * x 。
因此从小到大枚举可能的 Mex ,尽量补全缺失的数即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int T, n, x, a[N], cnt[N];
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &x);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 0; i <= n; i ++) cnt[i] = 0;
for(int i = 0, j = 1; i <= n; i ++) {
while(j <= n && a[j] <= i) cnt[a[j] % x] ++, j ++;
if(cnt[i % x]) cnt[i % x] --;
else {
printf("%d\n", i);
break;
}
}
}
return 0;
}
C2. Adjust The Presentation
一个结论, b 数组中每个数第一次出现的先后次序必须满足 a 数组中的先后次序。考虑对 b 数组做一个从 a 数组来的映射。
问题转化为 b 数组各个数第一次出现位置是否按照大小升序排列,每次单点修改。
可以维护下降断点的数量,每次修改只会改变 O(1) 个断点的状态。断点为 0 表示当前 b 数组符合条件。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
int T, n, m, q, a[N], pos[N], fst[N];
priority_queue<int, vector<int>, greater<int> > q1[N], q2[N];
bool is[N];
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; i ++){
while(q1[i].size()) q1[i].pop();
while(q2[i].size()) q2[i].pop();
is[i] = false;
}
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]), pos[a[i]] = i, q1[i].push(m + 1);
for(int i = 1; i <= m; i ++)
scanf("%d", &a[i]), a[i] = pos[a[i]];
for(int i = 1; i <= m; i ++)
q1[a[i]].push(i);
for(int i = 1; i <= n; i ++)
fst[i] = q1[i].top();
int cnt = 0;
for(int i = 1; i < n; i ++)
if(fst[i] > fst[i + 1]) is[i] = true, cnt ++;
puts(!cnt ? "YA" : "TIDAK");
for(int i = 1, x, t; i <= q; i ++) {
scanf("%d%d", &x, &t);
t = pos[t];
q2[a[x]].push(x);
while(q2[a[x]].size() && q1[a[x]].top() == q2[a[x]].top()) q1[a[x]].pop(), q2[a[x]].pop();
fst[a[x]] = q1[a[x]].top();
if(a[x] < n) {
if(fst[a[x]] <= fst[a[x] + 1] && is[a[x]]) is[a[x]] = false, cnt --;
if(fst[a[x]] > fst[a[x] + 1] && !is[a[x]]) is[a[x]] = true, cnt ++;
}
if(a[x] > 1) {
if(fst[a[x]] >= fst[a[x] - 1] && is[a[x] - 1]) is[a[x] - 1] = false, cnt --;
if(fst[a[x]] < fst[a[x] - 1] && !is[a[x] - 1]) is[a[x] - 1] = true, cnt ++;
}
a[x] = t;
q1[t].push(x);
while(q2[t].size() && q1[t].top() == q2[t].top()) q1[t].pop(), q2[t].pop();
fst[t] = q1[t].top();
if(t < n) {
if(fst[t] <= fst[t + 1] && is[t]) is[t] = false, cnt --;
if(fst[t] > fst[t + 1] && !is[t]) is[t] = true, cnt ++;
}
if(a[x] > 1) {
if(fst[t] >= fst[t - 1] && is[t - 1]) is[t - 1] = false, cnt --;
if(fst[t] < fst[t - 1] && !is[t - 1]) is[t - 1] = true, cnt ++;
}
puts(!cnt ? "YA" : "TIDAK");
}
}
return 0;
}
E1. Digital Village (Easy Version)
先 floyed 求出每两个点之间路径最大边权的最小值。
随后贪心地加入当前能使答案最小的点即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 405;
int T, n, m, p, dis[N][N], d[N], s[N];
bool vis[N];
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d%d", &n, &m, &p);
for(int i = 1; i <= n; i ++)
d[i] = 2e9, vis[i] = false;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
dis[i][j] = 2e9;
for(int i = 1; i <= n; i ++) dis[i][i] = 0;
for(int i = 1; i <= p; i ++)
scanf("%d", &s[i]);
for(int i = 1, u, v, w; i <= m; i ++) {
scanf("%d%d%d", &u, &v, &w);
dis[u][v] = dis[v][u] = min(dis[u][v], w);
}
for(int k = 1; k <= n; k ++)
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
if(i != j && j != k && k != i)
dis[i][j] = min(dis[i][j], max(dis[i][k], dis[k][j]));
long long ans = 1e18;
for(int i = 1; i <= n; i ++) {
int id = 0;
for(int j = 1; j <= n; j ++) {
if(vis[j]) continue;
long long res = 0;
for(int k = 1; k <= p; k ++)
res += min(d[s[k]], dis[j][s[k]]);
if(res < ans) ans = res, id = j;
}
printf("%lld ", ans);
vis[id] = true;
for(int j = 1; j <= p; j ++)
d[s[j]] = min(d[s[j]], dis[id][s[j]]);
}
puts("");
}
return 0;
}