[比赛记录] Codeforces Round #680 Div. 2
A. Array Rearrangment
把 \(a\) 从小到大排序, \(b\) 从大到小排序, 对所有 \(a_i + b_i\) 判断它们是否小于等于 \(x\) 即可.
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
const int _ = 50 + 7;
int n, X, a[_], b[_];
bool cmp(int i, int j) { return i > j; }
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> X;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> b[i];
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1, cmp);
bool flag = 1;
for (int i = 1; i <= n; ++i)
if (a[i] + b[i] > X) { flag = 0; break; }
puts(flag ? "Yes" : "No");
}
return 0;
}
B. Elimination
结论: 答案为 \(\max(a + b, c + d)\).
证明:
设 \(a + b > c + d\).
必要性: 因为现在已知至少有 100 个人的分数是大于等于 \(a+b\) 的, 所以如果一个人的分数小于 \(a+b\), 那他肯定不能在前 100 名内.
充分性: 因为 \(c+d < a+b\), 所以我们可以使第二场排名中的所有人的第一场得分为 \(c\),第二场得分为 \(d\), 并使第一场排名在 100 后的人的分数都小于 \(a +b\), 那么 \(a+b\) 就能进入前 100 名.
#include <cstdio>
#include <iostream>
using namespace std;
int T, a, b, c, d;
int main() {
cin >> T;
while (T--) {
cin >> a >> b >> c >> d;
cout << max(a + b, c + d) << endl;
}
return 0;
}
C. Division
考场上因为写法太 sb 导致炸了 long long, 然后就 gg 了
首先, 如果 \(q \not \mid p\) , 直接输出 \(p\) 即可.
如果要满足 \(q \not \mid x\), 那么 \(q\) 必然有一个 \(x\) 没有的因子 \(d\), 又因为 \(q \mid p\), 所以 \(p\) 除去 \(d\) 后就是一个合法的 \(x\). 枚举所有 \(d\) 后取个最大值就行了.
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
int T;
ll p, q, x;
int main() {
cin >> T;
while (T--) {
cin >> p >> q;
if (p % q) { cout << p << endl; continue; }
x = 0;
for (int i = 1; i * i <= q; ++i)
if (!(q % i)) {
if (i != 1) {
ll tmp = p; while (!(tmp % q)) tmp /= i;
x = max(x, tmp);
}
ll tmp = p; while (!(tmp % q)) tmp /= q / i;
x = max(x, tmp);
}
cout << x << endl;
}
return 0;
}
D. Divide and Sum
一道奇(shen)妙(xian)的题目.
结论: 所有分组的贡献都是一样的.
证明:
先假设所有 \(a_i\) 互不相同. 相同的情况可以感性推得.
先把 \(a\) 从小到大排序, 假设 \(i > j > n\) 并且 \(|a_i - a_j|\) 对答案有贡献, 那么就要有 \(n\) 个数大于 \(a_j\), 与 \(j > n\) 相矛盾. 所以不存在这种情况. \(n > i > j\) 的情况也类似.
所以, 对答案做出贡献的点对只可能是 \(i > n,\ j < n\) 的情况.
所以所有分组的贡献都是后 \(n\) 个 \(a_i\) 的和 - 前 \(n\) 个 \(a_i\) 的和, 答案乘个 \(\binom{2n}{n}\) 就好了.
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int _ = 3e5 + 7;
const int mod = 998244353;
int n, a[_], sum[2], fac[_], inv[_], ifac[_];
int main() {
cin >> n, n += n;
fac[0] = ifac[0] = 1, inv[1] = 1;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
fac[i] = (ll)fac[i - 1] * i % mod;
if (i != 1) inv[i] = (ll)inv[mod % i] * (mod - mod / i) % mod;
ifac[i] = (ll)ifac[i - 1] * inv[i] % mod;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n / 2; ++i) sum[0] = (sum[0] + a[i]) % mod;
for (int i = n / 2 + 1; i <= n; ++i) sum[1] = (sum[1] + a[i]) % mod;
cout << (ll)(sum[1] - sum[0] + mod) * fac[n] % mod * ifac[n / 2] % mod * ifac[n / 2] % mod << endl;
return 0;
}
E. Team-Building
先 bfs 一遍, 把有奇环的颜色判掉, 并对每个联通块求出它的染色关系, 可以用并查集维护连通块.
然后枚举每一类端点颜色不同的边, 根据这些边的连边关系和之前求出的染色关系进行黑白染色即可.
(说起来好像很简单, 但是我调了一个半小时...)
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define pb push_back
using namespace std;
typedef long long ll;
const int _ = 5e5 + 7;
const int __ = 1e6 + 7;
int n, m, K, col[_], ol[_], nw[_], fa[_], box[__], cnt, pl[_], num;
bool ban[_];
ll ans;
vector<int> to[_], tt[_], p[_];
queue<int> q;
struct EDGE { int u, v; } e[_];
void Init() {
cin >> n >> m >> K, num = K;
for (int i = 1; i <= n; ++i) scanf("%d", &col[i]), p[col[i]].pb(i);
for (int i = 1, x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
if (col[x] > col[y]) swap(x, y);
to[x].pb(y), to[y].pb(x);
e[i] = { x, y };
}
}
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
bool Bfs(int x) {
while (!q.empty()) q.pop();
ol[x] = 0, q.push(x);
while (!q.empty()) {
int u = q.front(); q.pop();
int fu = Find(u), fv;
for (int v: to[u]) {
if (col[v] != col[x]) continue;
fv = Find(v);
if (fu != fv) fa[fv] = fu;
if (ol[v] == -1) ol[v] = ol[u] ^ 1, q.push(v);
else if (ol[v] == ol[u]) { ans -= num - 1, --num, ban[col[x]] = 1; return 1; }
}
}
return 0;
}
void Dye(int x) {
int pt = pl[x] + 1, fx = Find(x);
while (Find(box[pt]) == fx) q.push(box[pt]), nw[box[pt]] = nw[x] ^ (ol[box[pt]] != ol[x]), ++pt;
pt = pl[x] - 1;
while (pt and Find(box[pt]) == fx) q.push(box[pt]), nw[box[pt]] = nw[x] ^ (ol[box[pt]] != ol[x]), --pt;
}
bool Work(int x) {
while (!q.empty()) q.pop();
nw[x] = 0, Dye(x), q.push(x);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v: tt[u]) {
if (nw[u] == nw[v]) return 1;
else if (nw[v] == -1) nw[v] = nw[u] ^ 1, Dye(v), q.push(v);
}
}
return 0;
}
bool cmp1(int i, int j) { return Find(i) == Find(j) ? i < j : Find(i) < Find(j); }
bool cmp(EDGE a, EDGE b) { return col[a.u] == col[b.u] ? col[a.v] < col[b.v] : col[a.u] < col[b.u]; }
void Run() {
ans = (ll)K * (K - 1) / 2;
memset(ol, -1, sizeof ol);
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= K; ++i)
for (int x: p[i])
if (ol[x] == -1 and Bfs(x)) {
break;
}
sort(e + 1, e + m + 1, cmp);
for (int i = 1, j; i <= m; i = j) {
j = i, cnt = 0;
while (col[e[j].u] == col[e[i].u] and col[e[j].v] == col[e[i].v]) {
box[cnt + 1] = e[j].u, box[cnt + 2] = e[j].v;
cnt += 2, ++j;
}
if (col[e[i].u] == col[e[i].v] or ban[col[e[i].u]] or ban[col[e[i].v]]) continue;
sort(box + 1, box + cnt + 1, cmp1);
cnt = unique(box + 1, box + cnt + 1) - box - 1;
for (int k = 1; k <= cnt; ++k) pl[box[k]] = k, nw[box[k]] = -1;
for (int k = i; k < j; ++k) tt[e[k].u].pb(e[k].v), tt[e[k].v].pb(e[k].u);
for (int k = i; k < j; ++k)
if (nw[e[k].u] == -1 and Work(e[k].u)) { --ans; break; }
for (int k = 1; k <= cnt; ++k) tt[box[k]].clear();
}
cout << ans << endl;
}
int main() {
Init();
Run();
return 0;
}