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;
}
posted @ 2022-10-10 13:36  Kobe303  阅读(55)  评论(0编辑  收藏  举报