2019-2020 ICPC, Asia Jakarta Regional Contest
Contest Info
[Practice Link](https://codeforces.com/contest/1252)
Solved | A | B | C | D | E | F | G | H | I | J | K | L |
---|---|---|---|---|---|---|---|---|---|---|---|---|
7/12 | O | - | O | - | O | - | O | O | - | O | O | - |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. Copying Homework
签到。
C. Even Path
题意:
给出一个\(n \cdot n\)的矩阵,再给出两个长度为\(n\)的序列\(b, c\),并且有\(a_{i, j} = b_i + c_j\),定义一条'Even Path'为一条经过的格子上面都是偶数的路径。
现在每次询问给出\(x_1, y_1, x_2, y_2\),询问\((x_1, y_1) \rightarrow (x_2, y_2)\)是否存在一条'Even Path'
思路:
发现路径的扩展每次都是扩展一行或者一列,那么当且仅当\(b[x_1] \cdots b[x_2]\)这一段数的奇偶性相同并且\(c[x_1] \cdots c[x_2]\)这一段数的奇偶性相同即可。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N], b[N], idA[N], idB[N];
int main() {
while (scanf("%d%d", &n, &q) != EOF) {
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
a[i] %= 2;
}
for (int i = 1; i <= n; ++i) {
scanf("%d", b + i);
b[i] %= 2;
}
idA[1] = 1;
idB[1] = 1;
for (int i = 2; i <= n; ++i) {
if (a[i] == a[i - 1]) {
idA[i] = idA[i - 1];
} else {
idA[i] = idA[i - 1] + 1;
}
if (b[i] == b[i - 1]) {
idB[i] = idB[i - 1];
} else {
idB[i] = idB[i - 1] + 1;
}
// cout << idA[i] << " " << idB[i] << endl;
}
int x[2], y[2];
while (q--) {
scanf("%d%d%d%d", x, y, x + 1, y + 1);
if (idA[x[0]] == idA[x[1]] && idB[y[0]] == idB[y[1]]) {
puts("YES");
} else {
puts("NO");
}
}
}
return 0;
}
E. Songwriter
题意:
给出一个序列\(a_i\),要求构造一个序列\(b_i\),满足\(b_i \in [L, R]\),并且\(b_i\)和\(b_{i + 1}\)的大小关系和\(a_i\)与\(a_{i + 1}\)相同
并且满足对于任意\(i \in [1, n - 1]\),都有\(|b_i - b_{i + 1}| \leq K\)。
输出满足要求的字典序最小的\(b_i\)
思路:
令\(l_n = L, r_n = R\),然后往前推出每个\(b_i\)的合法取值范围。
然后从前往后贪心即可。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, L, R, K, a[N], l[N], r[N];
void gao() {
l[n] = L, r[n] = R;
for (int i = n - 1; i >= 1; --i) {
if (a[i] == a[i + 1]) {
l[i] = l[i + 1];
r[i] = r[i + 1];
} else if (a[i] < a[i + 1]) {
r[i] = r[i + 1] - 1;
l[i] = max(L, l[i + 1] - K);
} else if (a[i] > a[i + 1]) {
l[i] = l[i + 1] + 1;
r[i] = min(R, r[i + 1] + K);
}
if (l[i] > r[i]) {
puts("-1");
return;
}
}
int x = l[1];
for (int i = 1; i <= n; ++i) {
printf("%d%c", x, " \n"[i == n]);
if (i < n) {
if (a[i] < a[i + 1]) {
x = max(l[i + 1], x + 1);
} else if (a[i] > a[i + 1]) {
x = max(x - K, l[i + 1]);
}
}
}
}
int main() {
while (scanf("%d%d%d%d", &n, &L, &R, &K) != EOF) {
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
}
gao();
}
return 0;
}
G. Performance Review
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m, q;
vector<vector<int> >G;
int a[N], rk[N], remind[N];
struct SEG {
struct node {
int Min;
int lazy;
node() {}
node(int _Min, int _lazy) {
Min = _Min;
lazy = _lazy;
}
void up(int x) {
Min += x;
lazy += x;
}
}t[N << 2];
void down(int id) {
int &lazy = t[id].lazy;
if (lazy) {
t[id << 1].up(lazy);
t[id << 1 | 1].up(lazy);
lazy = 0;
}
}
void pushup(int id) {
t[id].Min = min(t[id << 1].Min, t[id << 1 |1].Min);
}
void build(int id, int l, int r) {
t[id] = {0, 0};
if (l == r) {
t[id].Min = remind[l];
t[id].lazy = 0;
return ;
}
int mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
pushup(id);
}
void modify(int id, int l, int r, int ql, int qr, int v) {
if (ql > qr) return ;
if (l >= ql && r <= qr) {
t[id].up(v);
return ;
}
int mid = (l + r) >> 1;
down(id);
if (ql <= mid) modify(id << 1, l, mid, ql, qr, v);
if (qr > mid) modify(id << 1 | 1, mid + 1, r, ql, qr, v);
pushup(id);
}
int query(int id, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return t[id].Min;
int mid = (l + r);
down(id);
int res = INF;
if (ql <= mid) res = min(res, query(id << 1, l, mid, ql, qr));
if (qr > mid) res = min(res, query(id << 1 | 1, mid + 1, r, ql, qr));
return res;
}
}seg;
int main() {
while (scanf("%d %d %d", &n, &m, &q) != EOF) {
G.clear();
G.resize(m + 1);
rk[1] = 1;
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
if (a[i] > a[1]) {
rk[1]++;
}
}
for (int i = 1, r, b; i <= m; ++i) {
scanf("%d", &r);
rk[i + 1] = rk[i];
for (int j = 1; j <= r; ++j) {
scanf("%d", &b);
G[i].push_back(b);
if (b > a[1]) {
rk[i + 1]++;
}
}
remind[i] = n - r - rk[i];
}
seg.build(1, 1, m);
for (int _q = 1, x, y, z; _q <= q; ++_q) {
scanf("%d %d %d", &x, &y, &z);
int pre = G[x][y - 1], now = z;
if (pre < a[1] && now > a[1]) {
seg.modify(1, 1, m, x + 1, m, -1);
}
if (pre > a[1] && now < a[1]) {
seg.modify(1, 1, m, x + 1, m, 1);
}
int rank = seg.t[1].Min;
puts(rank < 0 ? "0" : "1");
G[x][y - 1] = z;
}
}
return 0;
}
H. Twin Buildings
题意:
给出\(n\)个地基,要造两个占地面积相同(长宽相同)的房子,问最大占地面积是多少。
可以建在两个地基上,也可以建在一个地基上
思路:
- 建在一个地基上:暴力枚举
- 建在两个地基上:容易发现,肯定有其中一个地基的一边被完整用上了,那么枚举这一边,然后在从所有最长边大于等于这个变成的地基中按另一边排序选最大和次大来建,这个用堆维护即可
代码:
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int n, ce;
struct E {
ll x, y; int id;
bool operator < (const E &other) const {
return x < other.x;
}
}e[N];
int main() {
while (scanf("%d", &n) != EOF) {
ce = 0;
for (int i = 1, x, y; i <= n; ++i) {
scanf("%d%d", &x, &y);
e[++ce] = {x, y, i};
e[++ce] = {y, x, i};
}
sort(e + 1, e + 1 + ce);
priority_queue <E> pq;
ll res = 0;
for (int i = ce; i >= 1; --i) {
res = max(res, e[i].x * e[i].y);
ll now = e[i].x;
pq.push({e[i].y, e[i].x, e[i].id});
while (pq.size() > 1) {
E t1 = pq.top(); pq.pop();
E t2 = pq.top(); pq.pop();
if (t1.id == t2.id) {
pq.push(t1);
} else {
pq.push(t1);
pq.push(t2);
break;
}
}
if (pq.size() > 1) {
E t1 = pq.top(); pq.pop();
E t2 = pq.top(); pq.pop();
res = max(res, now * t2.x * 2);
pq.push(t1);
pq.push(t2);
}
}
printf("%lld", res / 2);
puts((res % 2) ? ".5" : ".0");
}
return 0;
}
J. Tiling Terrace
题意:
给出一个字符串,里面只包含'.', '#':
- 如果拼成'.',那么会得到\(G_1\)的分数
- 如果拼成'..',那么会得到\(G_2\)的分数
- 如果拼成'.#.',那么会得到\(G_3\)的分数
现在要将该字符串分成若干段,每一段都是上述的某一个,使得分数最大,并且保证字符串中'#'的个数不超过\(50\)。
但是分段后,要满足第一种类型字符串不超过\(K\)个
思路:
考虑'..'可以换成两个'.',所以\(f[i][j]\)表示前\(i\)个字符,有\(j\)个第三种类型字符串的情况下,最多包含多少个第二类型字符串,然后枚举每种状态贪心即可。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int n, limit, pre[N], g[3], f[N][60];
char s[N];
ll get(ll x, ll y) {
if (x >= limit || g[0] * 2 <= g[1]) {
x = min(x, 1ll * limit);
return x * g[0] + y * g[1];
}
if (limit - x >= y * 2) {
x += y * 2;
return x * g[0];
}
if ((limit - x) % 2 == 0) {
int need = limit - x;
y -= need / 2;
x += need;
return x * g[0] + y * g[1];
} else {
int need = limit - x - 1;
y -= need / 2;
x += need;
--y;
return x * g[0] + y * g[1] + max(g[0], g[1]);
}
}
int main() {
while (scanf("%d%d", &n, &limit) != EOF) {
memset(f, 0, sizeof f);
for (int i = 0; i < 3; ++i) scanf("%d", g + i);
scanf("%s", s + 1);
memset(f, -0x3f, sizeof f);
f[0][0] = 0;
pre[0] = 0;
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] + (s[i] == '.');
for (int j = 0; j <= 50; ++j) {
f[i][j] = f[i - 1][j];
}
if (i > 1 && s[i] == '.' && s[i - 1] == '.') {
for (int j = 0; j <= 50; ++j) {
f[i][j] = max(f[i][j], f[i - 2][j] + 1);
}
}
if (i > 2 && s[i] == '.' && s[i - 1] == '#' && s[i - 2] == '.') {
for (int j = 0; j <= 50; ++j) {
f[i][j + 1] = max(f[i][j + 1], f[i - 3][j]);
}
}
}
ll res = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= 50 && f[i][j] >= 0; ++j) {
ll now = 1ll * j * g[2];
ll x = pre[i] - (j + f[i][j]) * 2;
ll y = f[i][j];
now += get(x, y);
res = max(res, now);
}
}
printf("%lld\n", res);
}
return 0;
}
K. Addition Robot
线段树维护矩阵乘法。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, mod = 1e9 + 7;
int n, q; char s[N];
struct Matrix {
int a[2][2];
void init() { a[0][0] = a[1][1] = 1; a[0][1] = a[1][0] = 0; }
int* operator[] (int x) { return a[x]; }
Matrix operator * (Matrix b) {
Matrix res;
res[0][0] = (1ll * a[0][0] * b[0][0] % mod + 1ll * a[0][1] * b[1][0] % mod) % mod;
res[0][1] = (1ll * a[0][0] * b[0][1] % mod + 1ll * a[0][1] * b[1][1] % mod) % mod;
res[1][0] = (1ll * a[1][0] * b[0][0] % mod + 1ll * a[1][1] * b[1][0] % mod) % mod;
res[1][1] = (1ll * a[1][0] * b[0][1] % mod + 1ll * a[1][1] * b[1][1] % mod) % mod;
return res;
}
}A, B, res;
struct SEG {
struct node {
Matrix a[2];
int lazy;
void init() {
a[0].init(); a[1].init();
lazy = 0;
}
void up() {
swap(a[0], a[1]);
lazy ^= 1;
}
}t[N << 2];
void pushup(int id) {
t[id].a[0] = t[id << 1 | 1].a[0] * t[id << 1].a[0];
t[id].a[1] = t[id << 1 | 1].a[1] * t[id << 1].a[1];
}
void down(int id) {
int &lazy = t[id].lazy;
if (lazy) {
t[id << 1].up();
t[id << 1 | 1].up();
lazy = 0;
}
}
void build(int id, int l, int r) {
if (l == r) {
t[id].a[0] = A;
t[id].a[1] = B;
if (s[l] == 'B') {
swap(t[id].a[0], t[id].a[1]);
}
return;
}
int mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
pushup(id);
}
void modify(int id, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) {
t[id].up();
return;
}
int mid = (l + r) >> 1;
down(id);
if (ql <= mid) modify(id << 1, l, mid, ql, qr);
if (qr > mid) modify(id << 1 | 1, mid + 1, r, ql, qr);
pushup(id);
}
Matrix query(int id, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return t[id].a[0];
int mid = (l + r) >> 1;
down(id);
Matrix res; res.init();
if (qr > mid) res = res * query(id << 1 | 1, mid + 1, r, ql, qr);
if (ql <= mid) res = res * query(id << 1, l, mid, ql, qr);
return res;
}
}seg;
int main() {
A = {1, 1, 0, 1};
B = {1, 0, 1, 1};
while (scanf("%d%d", &n, &q) != EOF) {
scanf("%s", s + 1);
seg.build(1, 1, n);
int op, l, r, a, b;
for (int i = 1; i <= q; ++i) {
scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
seg.modify(1, 1, n, l, r);
} else {
scanf("%d%d", &a, &b);
res = {a, 0, b, 0};
res = seg.query(1, 1, n, l, r) * res;
printf("%d %d\n", res[0][0], res[1][0]);
}
}
}
return 0;
}