AGC005 題解

Include_Z_F_R_qwq發表於2024-10-09

目錄
  • A - STring
  • B - Minimum Sum
  • C - Tree Restoring

A - STring

用棧模擬一下即可,具體的,當棧頂出現形如 ST 時,將其彈出。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
	int sig = 1;
	ll num = 0;
	char c = getchar();
	while(!isdigit(c)) {
		if(c == '-') {
			sig = -1;
		}
		c = getchar();
	}
	while(isdigit(c)) {
		num = (num << 3) + (num << 1) + (c ^ 48);
		c = getchar();
	}
	return num * sig;
}
void Write(ll x) {
	if(x < 0) {
		putchar('-');
		x = -x;
	}
	if(x >= 10) {
		Write(x / 10);
	}
	putchar((x % 10) ^ 48);
}

int main() {
	string s;
	cin >> s;
	stack<char> st;
	int i, n = s.size();
	for(i = 0; i < n; i++) {
		if(s[i] == 'T' && !st.empty() && st.top() == 'S') {
			st.pop();
		}
		else {
			st.emplace(s[i]);
		}
	}
	Write(st.size());
	return 0;
}

B - Minimum Sum

考慮將每個數的貢獻拆開,從最小的數開始考慮,包括這個最小數的區間的最小值一定是該數,而剩下的區間正好被最小值拆成兩半,可以遞迴地去做。
具體的,用 ST 表維護區間內最小值的位置,設這個最小值的位置為第 \(x\) 位,當前區間為 \([l, r]\),則貢獻為 \(a_x \cdot (r - x + 1) \cdot (x - l + 1)\),再遞迴區間 \([l, x - 1]\)\([x + 1, r]\)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
	int sig = 1;
	ll num = 0;
	char c = getchar();
	while(!isdigit(c)) {
		if(c == '-') {
			sig = -1;
		}
		c = getchar();
	}
	while(isdigit(c)) {
		num = (num << 3) + (num << 1) + (c ^ 48);
		c = getchar();
	}
	return num * sig;
}
void Write(ll x) {
	if(x < 0) {
		putchar('-');
		x = -x;
	}
	if(x >= 10) {
		Write(x / 10);
	}
	putchar((x % 10) ^ 48);
}

const int N = 200005, lgN = 20;
int n, a[N], lg[N];
void Init_lg() {
	int i;
	for(i = 2; i < N; i++) {
		lg[i] = lg[i >> 1] + 1;
	}
}
struct ST_Table {
	int f[lgN][N], g[lgN][N];
	void Init() {
		int i, j;
		for(i = 1; i <= n; i++) {
			f[0][i] = a[i], g[0][i] = i;
		}
		for(i = 1; i <= lg[n]; i++) {
			for(j = 1; j + (1 << i) - 1 <= n; j++) {
				if(f[i - 1][j] < f[i - 1][j + (1 << (i - 1))]) {
					f[i][j] = f[i - 1][j], g[i][j] = g[i - 1][j];
				}
				else {
					f[i][j] = f[i - 1][j + (1 << (i - 1))], g[i][j] = g[i - 1][j + (1 << (i - 1))];
				}
			}
		}
	}
	int Query(int l, int r) {
		int len = lg[r - l + 1];
		int x = f[len][l], y = f[len][r - (1 << len) + 1];
		return x < y ? g[len][l] : g[len][r - (1 << len) + 1];
	}
}st;
ll Solve(int l, int r) {
	if(l > r) {
		return 0;
	}
	int x = st.Query(l, r);
	return Solve(l, x - 1) + Solve(x + 1, r) + 1ll * (x - l + 1) * (r - x + 1) * a[x];
}
int main() {
	int i;
	n = Read();
	for(i = 1; i <= n; i++) {
		a[i] = Read();
	}
	Init_lg(), st.Init();
	Write(Solve(1, n));
	return 0;
}

C - Tree Restoring

回憶直徑的定義,注意到 \(\max a_i\) 即為直徑長度 \(d\),因此我們先建出這個直徑。
設直徑的兩個端點為 \(u, v\),根據直徑的性質,\(a_x = \max\{\operatorname{dis}(u, x), \operatorname{dis}(v, x)\}\)
對於 \(d\) 的奇偶性分類討論,對於 \(k\) 是偶數的情況,需要 \(\frac{d}{2} + 1 \sim d\)\(a_i\) 各兩個,\(\frac{d}{2}\)\(a_i\) 一個;對於 \(k\) 是奇數的情況,需要 \(\frac{d + 1}{2} \sim d\)\(a_i\) 各兩個。於是 \(a_i\) 不夠可以直接判無解。
對於剩下的點,我們可以在直徑上掛著,可以證明,當 \(k\) 是偶數時,掛的點 \(a_i\) 的取值範圍為 \([\frac{d}{2} + 1, d]\);當 \(k\) 是奇數時,掛的點 \(a_i\) 的取值範圍為 \([\frac{d + 3}{2}, d]\)
注意特判 \(d = 1\) 的情況。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll Read() {
	int sig = 1;
	ll num = 0;
	char c = getchar();
	while(!isdigit(c)) {
		if(c == '-') {
			sig = -1;
		}
		c = getchar();
	}
	while(isdigit(c)) {
		num = (num << 3) + (num << 1) + (c ^ 48);
		c = getchar();
	}
	return num * sig;
}
void Write(ll x) {
	if(x < 0) {
		putchar('-');
		x = -x;
	}
	if(x >= 10) {
		Write(x / 10);
	}
	putchar((x % 10) ^ 48);
}

const int N = 105;
int n, a[N], cnt[N];

int main() {
	int i, d = 0;
	n = Read();
	for(i = 1; i <= n; i++) {
		int a = Read();
		d = max(d, a);
		cnt[a]++;
	}
	if(d == 1) {
		printf(n > 2 ? "Impossible" : "Possible");
		return 0;
	}
	for(i = d; i > d / 2; i--) {
		if(cnt[i] < 2) {
			printf("Impossible");
			return 0;
		}
	}
	if(d % 2 == 0 && cnt[d / 2] != 1) {
		printf("Impossible");
		return 0;
	}
	if(d & 1 && cnt[(d + 1) / 2] != 2) {
		printf("Impossible");
		return 0;
	}
	for(i = 1; i < (d + 1) / 2; i++) {
		if(cnt[i]) {
			printf("Impossible");
			return 0;
		}
	}
	printf("Possible");
	return 0;
}