ABC 223 覆盤
[ABC223C] Doukasen
思路解析
根據題目可知,燃燒的總時長肯定不變,所以我們可以直接從頭開始遍歷找到第一根香使得燒完這根香後的時間會大於總時長的一半,然後加上剩餘時間下會燒掉的長度即可。
時間複雜度:一次遍歷,\(O(N)\)。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, a[N], b[N];
double t[N], st = 0;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d%d", &a[i], &b[i]);
t[i] = 1.0 * a[i] / b[i];
st += t[i];
}
double ans = 0, s = 0;
for(int i = 1; i <= n; i++) {
if(s + t[i] > st / 2.0) {
ans += ((st / 2.0) - s) * b[i];
break;
}
else ans += a[i], s += t[i];
}
printf("%.15lf", ans);
return 0;
}
[ABC223D] Restricted Permutation
思路解析
題目說的很明白,給定 \(m\) 個約束條件,求排列,很版的一道拓撲。可見這是一道拓撲排序板子,唯一不同的地方就在於一定要字典序最小,我們只需要講普通拓撲用的普通佇列改成優先佇列即可。
時間複雜度:由於有優先佇列自帶 \(\log\) 複雜度,同時每個點最多入隊一次,複雜度為 \(O(N \log N)\)。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, ind[N], d[N], ans[N];
vector<int> g[N];
map<int, int> mp[N];
bool cmp(int x, int y) {
if(d[x] != d[y]) return d[x] < d[y];
else return x < y;
}
int main() {
cin >> n >> m;
for(int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
g[u].push_back(v);
mp[u][v] = 1;
ind[v]++;
}
priority_queue<int, vector<int>, greater<int>> q;
for(int i = 1; i <= n; i++) {
if(ind[i] == 0) d[i] = 1, q.push(i);
}
vector<int> v;
while(!q.empty()) {
int u = q.top(); q.pop();
v.push_back(u);
for(auto it : g[u]) {
ind[it]--;
if(ind[it] == 0) {
d[it] = d[u] + 1;
q.push(it);
}
}
}
for(int i = 1; i <= n; i++) {
ans[i] = i;
if(d[i] == 0) {puts("-1"); return 0;}
}
for(auto it : v) cout << it << ' ';
return 0;
}
[ABC223E] Placing Rectangles
思路解析
根據題目可知,其實三個長方形無非只有以下兩種擺放方式。
若大長方形長為 \(y\),寬為 \(x\),則我們對於第一種情況就固定住寬,判斷能否使長度小於等於長;對於第二種情況同樣固定住寬,此時 A 長方形右邊空間的長就確定了,就只需要判斷 B,C 的寬之和能否小於大長方形的寬即可。
注意大長方形的長寬可以互換,小長方形的順序可以互換。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
ll x, y, a, b, c;
bool check() {
ll ax = ceil((double)a / x), bx = ceil((double)b / x), cx = ceil((double)c / x);
if(ax + bx + cx <= y) return true;
ll yy = y - ax;
if(yy <= 0) return false;
ll by = ceil((double)b / yy), cy = ceil((double)c / yy);
if(by + cy <= x) return true;
return false;
}
int main() {
cin >> x >> y >> a >> b >> c;
bool ans = check();
swap(a, b); ans |= check();
swap(a, c); ans |= check();
swap(x, y); ans |= check();
swap(a, b); ans |= check();
swap(a, c); ans |= check();
if(ans) puts("Yes");
else puts("No");
return 0;
}
[ABC223F] Parenthesis Checking
思路解析
在開始之前,首先我們需要知道合法括號序列的判斷方法。我們可以給每個括號打上權值,設左括號權值為 \(1\),右括號權值為 \(-1\),這樣一個 \(\texttt{(()())}\) 括號串用數字存下就是 \(1,1,-1,1,-1,-1\),這時我們再給序列計算一下字首和就成了 \(1,2,1,2,1,0\)。此時我們發現序列有一個性質就是元素全部大於等於 \(0\),同時結尾的元素一定為 \(0\)。而例如我們找一個括號序列 \(\texttt{)()(}\),它的字首和陣列為 \(-1,0,-1,0\),可見雖然結尾是 \(0\),但中間有元素小於 \(0\),因此該括號序列並不合法。
現在我們已經知道了如何透過序列的字首和陣列判斷該序列是否合法,接下來我們就考慮如何修改。我們可以先把字首和陣列存下來,然後考慮每一次交換會對這個陣列產生怎樣的影響。首先,若交換 \(l,r\) 兩個位置上的括號,我們可以發現區間 \([1,l-1]\) 和 \([r,n]\) 是沒有影響的,真正有影響的只有 \([l,r-1]\) 這個區間。接下來我們分兩種情況考慮:
- 若 \(l\) 位上的括號交換前是左括號時,可見由於第 \(l\) 位從 \(1\) 改成了 \(-1\),所以就會對該區間加上 \(-2\) 的權值。
- 若 \(l\) 位上的括號交換前是右括號時,可見由於第 \(l\) 位從 \(-1\) 改成了 \(1\),所以就會對該區間加上 \(2\) 的權值。
最後考慮如何實現,我們需要做到區間求最小值,單點查詢,區間加三種操作,可以想到使用線段樹維護。注意由於我們存的值是字首和,所以需要減去 \(l-1\) 位置上的值才能得到正確的值。記得特判 \(l-1=0\) 時的情況。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 8e5 + 10;
string str;
int n, q, a[N], s[M], t[M];
void push_up(int p) {
int ls = (p << 1), rs = (p << 1) + 1;
s[p] = min(s[ls], s[rs]);
}
void build(int p, int l, int r) {
if(l == r) {
s[p] = a[l];
return;
}
int m = l + ((r - l) >> 1), ls = (p << 1), rs = (p << 1) + 1;
build(ls, l, m);
build(rs, m + 1, r);
push_up(p);
}
void addt(int p, int l, int r, int k) {
s[p] += k;
t[p] += k;
}
void push_down(int p, int l, int r) {
if(!t[p]) return;
int m = l + ((r - l) >> 1), ls = (p << 1), rs = (p << 1) + 1;
addt(ls, l, m, t[p]);
addt(rs, m + 1, r, t[p]);
t[p] = 0;
}
void add(int p, int l, int r, int x, int y, int k) {
if(r < x || l > y) return;
if(l >= x && r <= y) {
addt(p, l, r, k);
return;
}
push_down(p, l, r);
int m = l + ((r - l) >> 1), ls = (p << 1), rs = (p << 1) + 1;
add(ls, l, m, x, y, k);
add(rs, m + 1, r, x, y, k);
push_up(p);
}
int ask(int p, int l, int r, int x, int y) {
if(r < x || l > y) return 2e9;
if(l >= x && r <= y) return s[p];
push_down(p, l, r);
int m = l + ((r - l) >> 1), ls = (p << 1), rs = (p << 1) + 1;
return min(ask(ls, l, m, x, y), ask(rs, m + 1, r, x, y));
}
int main() {
cin >> n >> q;
cin >> str;
str = ' ' + str;
for(int i = 1; i <= n; i++) {
a[i] = a[i - 1];
if(str[i] == '(') a[i]++;
else a[i]--;
}
build(1, 1, n);
while(q--) {
int op, l, r;
cin >> op >> l >> r;
if(op == 1) {
if(str[l] != str[r]) {
if(str[l] == '(') add(1, 1, n, l, r - 1, -2);
else add(1, 1, n, l, r - 1, 2);
swap(str[l], str[r]);
}
}
else {
int tmp = ask(1, 1, n, l - 1, l - 1);
if(l == 1) tmp = 0;
int t1 = ask(1, 1, n, l, r - 1) - tmp, t2 = ask(1, 1, n, r, r) - tmp;
if(t1 >= 0 && t2 == 0) cout << "Yes\n";
else cout << "No\n";
}
}
return 0;
}