目錄
- 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;
}