[賽記] 多校A層衝刺NOIP2024模擬賽06

Peppa_Even_Pig發表於2024-10-13

小 Z 的手套(gloves)100pts

最大值最小,考慮二分答案;

首先排序,然後每次找出數量較少的那個陣列中的每個數 $ x $ 在另一個陣列中有沒有值在範圍 $ [x - mid, x + mid] $ 的(其中 $ mid $ 為二分的答案),其實只需找 $ x - mid $ 就行,最後判斷一下所有數是否合法即可;

因為已經升序排序,所以可以雙指標維護,當然也可以 lower_bound,但是多個 $ \log $;

時間複雜度;$ \Theta(n \log Z) $ 到 $ \Theta(n \log Z \log n) $ 不等(其中 $ Z $ 為兩個陣列的極差);

點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int n, m;
int a[500005], b[500005];
int vis[500005], vi[500005];
bool ck(int x) {
	for (int i = 1; i <= max(n, m); i++) {
		vis[i] = 0;
		vi[i] = 0;
	}
	if (n < m) {
		int now = lower_bound(b + 1, b + 1 + m, a[1] - x) - b;
		for (int i = 1; i <= n; i++) {
			int lpos = lower_bound(b + 1, b + 1 + m, a[i] - x) - b;
			if (lpos > m) return false;
			if (now < lpos) now = lpos;
			while(vis[now]) now++;
			vis[now] = i;
		}
		for (int i = 1; i <= m; i++) {
			if (vis[i]) {
				vi[vis[i]] = true;
				if (abs(a[vis[i]] - b[i]) > x) return false;
			}
		}
		for (int i = 1; i <= n; i++) if (!vi[i]) return false;
		return true;
	} else {
		int now = lower_bound(a + 1, a + 1 + n, b[1] - x) - a;
		for (int i = 1; i <= m; i++) {
			int lpos = lower_bound(a + 1, a + 1 + n, b[i] - x) - a;
			if (lpos > n) return false;
			if (now < lpos) now = lpos;
			while(vis[now]) now++;
			vis[now] = i;
		}
		for (int i = 1; i <= n; i++) {
			if (vis[i]) {
				vi[vis[i]] = true;
				if (abs(b[vis[i]] - a[i]) > x) return false;
			}
		}
		for (int i = 1; i <= m; i++) if (!vi[i]) return false;
		return true;
	}
}
int main() {
	freopen("gloves.in", "r", stdin);
	freopen("gloves.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= m; i++) {
		cin >> b[i];
	}
	sort(a + 1, a + 1 + n);
	sort(b + 1, b + 1 + m);
	int l = 0;
	int r = 1000000000;
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if (ck(mid)) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	cout << ans;
	return 0;
}

小 Z 的字串(string)20pts

DP;

設 $ f_{i, j, k, 0/1/2} $ 表示現在選了 $ i $ 個 $ 0 $, $ j $ 個 $ 1 $ ,$ k $ 個 $ 2 $,當前是 $ 0/1/2 $ 的最小次數;

對於轉移,發現肯定不會換同一個數,所以假設有轉移 $ f_{i, j, k - 1, 0} \rightarrow f_{i, j, k, 2} $,我們只需將第 $ k $ 個數移動到當前位置 $ (i + j + k) $ 即可,然後計算貢獻(注意絕對值),其它同理;

最後答案要除以 $ 2 $,因為假設有兩個數能夠被轉移,它們兩個的相對位置是不變的,也就是說前面的由後面的轉移過來,後面的也由前面的轉移過來,所以要除以 $ 2 $;

時間複雜度:$ \Theta(n^3) $;

點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n;
char s[505];
int t[3][405];
int f[205][205][205][3];
int c[3];
int main() {
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> (s + 1);
	n = strlen(s + 1);
	for (int i = 1; i <= n; i++) {
		t[s[i] - '0'][++c[s[i] - '0']] = i;
	}
	if (c[0] > (n + 1) / 2 || c[1] > (n + 1) / 2 || c[2] > (n + 1) / 2) {
		cout << -1;
		return 0;
	}
	memset(f, 0x3f, sizeof(f));
	f[0][0][0][0] = f[0][0][0][1] = f[0][0][0][2] = 0;
	for (int i = 0; i <= c[0]; i++) {
		for (int j = 0; j <= c[1]; j++) {
			for (int k = 0; k <= c[2]; k++) {
				int p = i + j + k;
				if (p == 0) continue;
				if (i) f[i][j][k][0] = min(f[i - 1][j][k][1], f[i - 1][j][k][2]) + abs(p - t[0][i]);
				if (j) f[i][j][k][1] = min(f[i][j - 1][k][0], f[i][j - 1][k][2]) + abs(p - t[1][j]);
				if (k) f[i][j][k][2] = min(f[i][j][k - 1][0], f[i][j][k - 1][1]) + abs(p - t[2][k]);
			}
		}
	}
	cout << min({f[c[0]][c[1]][c[2]][0], f[c[0]][c[1]][c[2]][1], f[c[0]][c[1]][c[2]][2]}) / 2;
	return 0;
}

一個真實的故事(truth)50pts

賽時打的 $ \Theta(\frac{nm \log^2 n}{w}) $ 結果算的時候少算倆 $ \log $,所以50pts;

正解就是線段樹;

維護三個東西:

  1. 答案;

  2. 從左邊開始的1 ~ k 出現的位置;

  3. 從右邊開始的1 ~ k 出現的位置;

這樣合併的時候只需將左區間的2和右區間的3合併起來,然後雙指標掃一下即可;

時間複雜度:$ \Theta(nk \log n \log k) $,使用 $ sort $ 時可能會把後面的 $ \log $ 去掉;

點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n, k, m;
int a[500005];
int cnt[35];
pair<int, int> rem[75];
int rcnt;
namespace SEG{
	inline int ls(int x) {
		return x << 1;
	}
	inline int rs(int x) {
		return x << 1 | 1;
	}
	struct sss{
		int l, r, ans, lb[35], rb[35];
	}tr[800005];
	inline void push_up(int id) {
		memset(cnt, 0, sizeof(cnt));
		rcnt = 0;
		for (int i = 1; i <= k; i++) {
			rem[++rcnt] = {tr[ls(id)].rb[i], i};
			rem[++rcnt] = {tr[rs(id)].lb[i], i};
		}
		sort(rem + 1, rem + 1 + rcnt);
		int now = 0;
		int an = 0x3f3f3f3f;
		int pos = 1;
		for (int i = 1; i <= rcnt; i++) {
			if (rem[i].first) {
				pos = i;
				break;
			}
		}
		int j = pos;
		for (int i = pos; i <= rcnt; i++) {
			if (!cnt[rem[i].second]) now++;
			cnt[rem[i].second]++;
			while(now == k) {
				an = min(an, rem[i].first - rem[j].first + 1);
				cnt[rem[j].second]--;
				if (cnt[rem[j].second] == 0) now--;
				j++;
			}
		}
		tr[id].ans = min({an, tr[ls(id)].ans, tr[rs(id)].ans});
		for (int i = 1; i <= k; i++) {
			if (tr[ls(id)].lb[i]) tr[id].lb[i] = tr[ls(id)].lb[i];
			else tr[id].lb[i] = tr[rs(id)].lb[i];
			if (tr[rs(id)].rb[i]) tr[id].rb[i] = tr[rs(id)].rb[i];
			else tr[id].rb[i] = tr[ls(id)].rb[i];
		}
	}
	void bt(int id, int l, int r) {
		tr[id].l = l;
		tr[id].r = r;
		tr[id].ans = 0x3f3f3f3f;
		if (l == r) {
			tr[id].lb[a[l]] = tr[id].rb[a[l]] = l;
			if (k == 1) {
				if (a[l] == k) tr[id].ans = 1;
			}
			return;
		}
		int mid = (l + r) >> 1;
		bt(ls(id), l, mid);
		bt(rs(id), mid + 1, r);
		push_up(id);
	}
	void add(int id, int pos, int x) {
		if (tr[id].l == tr[id].r) {
			tr[id].ans = 0x3f3f3f3f;
			if (k == 1) {
				if (a[tr[id].l] == k) tr[id].ans = 1;
			}
			tr[id].lb[x] = tr[id].rb[x] = 0;
			tr[id].lb[a[tr[id].l]] = tr[id].rb[a[tr[id].l]] = tr[id].l;
			return;
		}
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (pos <= mid) add(ls(id), pos, x);
		else add(rs(id), pos, x);
		push_up(id);
	}
}
using namespace SEG;
int main() {
	freopen("truth.in", "r", stdin);
	freopen("truth.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> k >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	bt(1, 1, n);
	int s, p, v;
	for (int i = 1; i <= m; i++) {
		cin >> s;
		if (s == 1) {
			cin >> p >> v;
			int x = a[p];
			a[p] = v;
			add(1, p, x);
		}
		if (s == 2) {
			if (tr[1].ans == 0x3f3f3f3f) cout << -1 << '\n';
			else cout << tr[1].ans << '\n';
		}
	}
	return 0;
}

相關文章