COCI 2021-2022 #6
COCI 2021-2022 #6 题解
T1:
简单模拟
T2:
枚举子矩阵的第一行和最后一行,按列双指针。
预处理每列的前缀和,时间复杂度为 \(\mathcal O(n^3)\)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 505;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m, x, y;
ll a[N][N];
ll ans = inf;
int main() {
scanf("%d%d%d%d", &n, &m, &x, &y);
if (x > y) swap(x, y);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%lld", &a[i][j]), a[i][j] += a[i - 1][j];
for (int i = 1; i <= n; ++i) for (int j = i; j <= n; ++j) {
ll cur = 0; int l = 1, r = 1;
while (r <= m) {
while (l < r && cur > y) cur -= a[j][l] - a[i - 1][l], ans = min(ans, abs(cur - x) + abs(cur - y)), ++l;
if (x <= cur && cur <= y) return printf("%d", y - x), 0;
while (r <= m && cur <= y) cur += a[j][r] - a[i - 1][r], ans = min(ans, abs(cur - x) + abs(cur - y)), ++r;
}
while (l <= m) cur -= a[j][l] - a[i - 1][l], ans = min(ans, abs(cur - x) + abs(cur - y)), ++l;
}
printf("%lld", ans);
return 0;
}
T3:
题面很花里胡哨但是实际是诈骗题。
就一拓扑板子。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 200005, M = 500005;
int n, m;
int head[N], ver[M], nxt[M], cnt;
int deg[N];
int ans[N], tot;
void add(int u, int v) {
ver[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= m; ++i) scanf("%d%d", &u, &v), add(v, u), ++deg[u];
queue <int> q;
for (int i = 1; i <= n; ++i) if (!deg[i]) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
ans[++tot] = u;
for (int i = head[u]; i; i = nxt[i]) {
int v = ver[i];
if (!--deg[v]) q.push(v);
}
}
if (tot != n) printf("%d", -1);
else {
printf("%d\n", n);
for (int i = 1; i <= n; ++i) printf("%d 1\n", ans[i]);
}
return 0;
}
T4:
PAM 启发式合并,自己有些细节还没看懂。
但是先贴个代码吧
Code:
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
typedef vector <int> vi;
const int N = 100005;
int n, fa[N];
char str[N];
struct deq {
vi l, r;
void push_front(int c) { l.pb(c); }
void push_back(int c) { r.pb(c); }
int get(int p) { return p < 1 || p > (int)(l.size() + r.size()) ? -1 : (p <= (int)l.size() ? l[l.size() - p] : r[p - l.size() - 1]); }
vi get_str() {
vi res = l;
reverse(res.begin(), res.end());
res.insert(res.end(), r.begin(), r.end());
return res;
}
};
struct PAM {
deq a;
vi fail, len;
vector <array <int, 2> > ch;
int pl, pr, cnt, t;
void init() {
cnt = pl = pr = 1;
fail.resize(2, 0), fail[0] = 1;
len.resize(2, 0), len[1] = -1;
ch.resize(2, {0, 0});
}
void New() {
++cnt;
fail.pb(0), len.pb(0), ch.pb({0, 0});
}
void push_back(int c) {
++t, a.pb(c);
while (a.get(t - 1 - len[pr]) != c) pr = fail[pr];
if (!ch[pr][c]) {
New();
for (int x = fail[pr]; ; x = fail[x])
if (a.get(t - 1 - len[x]) == c) {
fail[cnt] = ch[x][c];
break;
}
ch[pr][c] = cnt, len[cnt] = len[pr] + 2;
}
pr = ch[pr][c];
if (len[pr] == t) pl = pr;
}
void push_front(int c) {
++t, a.push_front(c);
while (a.get(len[pl] + 2) != c) pl = fail[pl];
if (!ch[pl][c]) {
New();
for (int x = fail[pl]; ; x = fail[x])
if (a.get(len[x] + 2) == c) {
fail[cnt] = ch[x][c];
break;
}
ch[pl][c] = cnt, len[cnt] = len[pl] + 2;
}
pl = ch[pl][c];
if (len[pl] == t) pr = pl;
}
void add_back(deq &a) {
vi str = a.get_str();
for (int c : str) push_back(c);
}
void add_front(deq &a) {
vi str = a.get_str();
reverse(str.begin(), str.end());
for (int c : str) push_front(c);
}
} f[N];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
int main() {
scanf("%d%s", &n, str + 1);
for (int i = 1; i <= n; ++i) f[i].init(), f[i].push_back(str[i] - '0'), fa[i] = i;
while (--n) {
int x, y; scanf("%d%d", &x, &y);
x = find(x), y = find(y);
if (f[x].t > f[y].t) {
f[x].add_back(f[y].a), fa[y] = x;
printf("%d\n", f[x].cnt - 1);
}
else {
f[y].add_front(f[x].a), fa[x] = y;
printf("%d\n", f[y].cnt - 1);
}
}
return 0;
}
T5:
首先明确合法的括号序列的条件。
若将 (
看成 \(1\),)
看成 \(-1\),那么一个括号序列合法当且仅当任意前缀和不小于 \(0\) 且最终的前缀和是 \(0\)。
有个比较感性的贪心思路,尽量将 (
放在左边,)
放在右边。
那么如果 \(s_{a_i}=s_{b_i}\),那么这些位置的策略已经固定了。
否则先取 (
,然后贪心的去修改。
具体细节看代码。
Code:
#include <bits/stdc++.h>
using namespace std;
#define NO return printf("%d\n", -1), void()
const int N = 200005;
int T;
int n;
char s[N];
int a[N], b[N];
int id[N], ans[N];
int c[N];
void add(int x, int y) { for (; x <= n * 2; x += x & -x) c[x] += y; }
int query(int x) { int res = 0; for (; x; x -= x & -x) res += c[x]; return res; }
struct node {
int id, x, y;
node (){}
node (int _id, int _x, int _y) {
id = _id, x = _x, y = _y;
}
bool operator < (const node &rhs) const {
return y > rhs.y;
}
};
priority_queue <node> q;
void solve() {
scanf("%d%s", &n, s + 1);
for (int i = 1; i <= n; ++i) scanf("%d%d", &a[i], &b[i]);
if (n & 1) NO;
memset(id + 1, 0, sizeof (int) * (2 * n));
memset(c + 1, 0, sizeof (int) * (2 * n));
while (!q.empty()) q.pop();
for (int i = 1; i <= n; ++i) {
if (s[a[i]] == s[b[i]]) {
if (s[a[i]] == '(')
ans[i] = 0, add(a[i], 1);
else
ans[i] = 1, add(b[i], -1);
}
else {
if (s[a[i]] == '(')
ans[i] = 0, add(a[i], 1);
if (s[b[i]] == '(')
ans[i] = 1, add(b[i], 1);
id[a[i]] = i;
}
}
for (int i = 1; i <= 2 * n; ++i) {
if (id[i]) {
int t = id[i];
add(a[t], -1), add(b[t], -1), ans[t] ^= 1;
q.push(node(t, a[t], b[t]));
}
while (!q.empty() && query(i) < 0) {
node u = q.top(); q.pop();
add(u.x, 1), add(u.y, 1), ans[u.id] ^= 1;
}
if (query(i) < 0) NO;
}
if (query(2 * n)) NO;
for (int i = 1; i <= n; ++i) printf("%d ", ans[i]);
printf("\n");
}
int main() {
scanf("%d", &T);
while (T--) solve();
return 0;
}