A. Perpendicular Segments
顯然構造一個 \(k \times k\) 的左下角是原點的正方形即可。
void slv() {
int x, y, k; Read(x, y, k);
int mn = min(x, y);
if (mn * mn * 2 >= k * k)
Write(0, ' ', 0, ' ', mn, ' ', mn, '\n'),
Write(0, ' ', mn, ' ', mn, ' ', 0, '\n');
return;
}
B. Black Cells
偶數的情況一定是相鄰兩兩匹配,奇數時可以空出來一個。
預處理前字尾 min 即可。
constexpr int N = 2e3 + 5;
int n;
ll a[N], pre[N], suf[N];
void slv() {
Read(n);
for (int i = 1; i <= n; i ++) a[i] = Read<ll>();
if (n == 1) { Puts("1"); return; }
if (n & 1) {
for (int i = 2; i <= n; i += 2)
pre[i] = max(pre[i - 2], a[i] - a[i - 1]);
for (int i = n - 1; i >= 1; i -= 2)
suf[i] = max(suf[i + 2], a[i + 1] - a[i]);
ll ans = 1e18;
for (int i = 2; i <= n + 1; i += 2)
cmin(ans, max(suf[i], pre[i - 2]));
Write(ans, '\n');
} else {
ll ans = 0;
for (int i = 2; i <= n; i += 2)
cmax(ans, a[i] - a[i - 1]);
Write(ans, '\n');
}
return;
}
C. Action Figures
一定是讓後面的 1 儘量免費,所以從後往前貪即可。
constexpr int N = 4e5 + 5;
int n;
char s[N];
bool vis[N], mrk[N];
void slv() {
Read(n), Read(s + 1);
ll ans = 0; int j = n, o = 0;
for (int i = n; i >= 1; i --) {
if (s[i] == '1' && !vis[i]) {
if (!o) {
while (j >= i) j --;
while (j >= 1 && (s[j] == '1' || vis[j])) j --;
if (j) mrk[i] = true, vis[j] = true, j --;
else o = 1, j = 1;
}
if (o) {
while (j < i && vis[j]) j ++;
if (j < i && !vis[j]) vis[j] = true, mrk[i] = true;
}
}
}
for (int i = 1; i <= n; i ++)
if (!mrk[i]) ans += i;
Write(ans, '\n');
return;
}
D. Sums of Segments
按長度為 \(1, 2, \cdots, n\) 分塊,定位 \(l\) 和 \(r\) 所在的塊可以 lower_bound。
將詢問差分成 \([1, r] - [1, l)\)。
對於整塊的貢獻可以直接字首和預處理。
散塊的貢獻可以透過維護 \(a_i \cdot i\) 的字首和來實現快速查詢。
constexpr int N = 3e5 + 5;
int n, q, a[N];
ll cnt[N], sum[N], prod[N], sigm[N];
void slv() {
Read(n);
for (int i = 1; i <= n; i ++) Read(a[i]), cnt[i] = n - i + 1;
partial_sum(cnt + 1, cnt + n + 1, cnt + 1);
for (int i = n; i >= 1; i --)
sum[i] = sum[i + 1] + 1ll * a[i] * (n - i + 1);
for (int i = 1; i <= n; i ++) sum[i] += sum[i - 1];
for (int i = 1; i <= n; i ++)
prod[i] = prod[i - 1] + i * a[i];
for (int i = 1; i <= n; i ++)
sigm[i] = sigm[i - 1] + a[i];
Read(q);
auto calc = [&](int l, int L, int R) {
ll ans = (sigm[L - 1] - sigm[l - 1]) * (R - L + 1);
return ans + (R + 1) * (sigm[R] - sigm[L - 1])
- (prod[R] - prod[L - 1]);
};
while (q --) {
ll l, r; Read(l, r);
int posl = lower_bound(cnt + 1, cnt + n + 1, l) - cnt;
int posr = lower_bound(cnt + 1, cnt + n + 1, r) - cnt;
ll ans = 0;
l = l - cnt[posl - 1] + posl - 1;
r = r - cnt[posr - 1] + posr - 1;
if (posl < posr) ans += sum[posr - 1] - sum[posl];
if (posl == posr) ans += calc(posl, l, r);
else ans += calc(posl, l, n) + calc(posr, posr, r);
Write(ans, '\n');
}
return;
}
E. Best Subsequence
看成選一個數有 1 的貢獻,選一個二進位制位有 1 的代價,選一個數就一定要選它對應的二進位制位。
這樣就轉化成了最大權閉合子圖,直接流。
void slv() {
static constexpr int INF = 1e9;
int n; Read(n);
MaxFlow_Graph<int> G(n + 60 + 2);
int S = n + 60, T = S + 1;
for (int i = 0; i < n; i ++) G.Add_Edge(S, i, 1);
for (int i = 0; i < 60; i ++) G.Add_Edge(n + i, T, 1);
for (int i = 0; i < n; i ++) {
ll x; Read(x);
for (int j = 0; j < 60; j ++)
if (x >> j & 1) G.Add_Edge(i, j + n, INF);
}
Write(n - G.Max_Flow(S, T), '\n');
return;
}
F. Bermart Ice Cream
先考慮如果只有一家商店怎麼做。
問題變成了維護一個物品序列,支援 push back、pop front、求揹包。
這個可以透過用棧模擬 queue 來完成。
對於原問題,可以離線建操作樹,然後在操作樹上 DFS。
現在需要支援 push back、push front、pop back、pop front、求揹包。
這個可以透過兩個棧模擬 deque 完成。
時間複雜度 \(O(q \max p)\)。
constexpr int N = 2e3 + 1, M = 3e4 + 5, INF = 1e9;
int n, m, q, id[M], ans[M];
vector<int> G[M];
vector<pair<int, int>> qry[M];
tuple<int, int, int> upd[M];
array<int, N> operator * (array<int, N> x, pii y) {
for (int i = N - 1; i >= y.fir; i --)
cmax(x[i], x[i - y.fir] + y.sec);
return x;
}
struct Stack {
vector<pair<int, int>> stk;
vector<array<int, N>> ans;
Stack() {
array<int, N> ori;
for (int i = 0; i < N; i ++) ori[i] = 0;
ans.emplace_back(ori); return;
}
void Push(pii x) {
stk.emplace_back(x);
auto ret = ans.back() * x;
ans.emplace_back(ret); return;
}
void Pop() { ans.pop_back(), stk.pop_back(); return; }
pii Top() { return stk.back(); }
int Query(int x) { return ans.back()[x]; }
int Size() { return stk.size(); }
bool Empty() { return stk.empty(); }
};
struct Deque {
Stack stk[3];
Deque() { return; }
void Move(int o1, int o2) {
if (stk[o2].Size()) return;
int cnt = stk[o1].Size() >> 1;
while (cnt --) stk[2].Push(stk[o1].Top()), stk[o1].Pop();
while (stk[o1].Size()) stk[o2].Push(stk[o1].Top()), stk[o1].Pop();
while (stk[2].Size()) stk[o1].Push(stk[2].Top()), stk[2].Pop();
return;
}
int Query(int x) {
if (stk[0].Empty() && stk[1].Empty()) return 0;
if (stk[0].Empty()) return stk[1].Query(x);
if (stk[1].Empty()) return stk[0].Query(x);
int ans = - INF;
for (int i = 0; i <= x; i ++)
cmax(ans, stk[0].Query(i) + stk[1].Query(x - i));
return ans;
}
void Push_Front(pii x) { stk[0].Push(x); return; }
void Push_Back(pii x) { stk[1].Push(x); return; }
void Pop_Front() { Move(1, 0); stk[0].Pop(); return; }
void Pop_Back() { Move(0, 1); stk[1].Pop(); return; }
pii Front() { Move(1, 0); return stk[0].Top(); }
pii Back() { Move(0, 1); return stk[1].Top(); }
int Size() { return stk[0].Size() + stk[1].Size(); }
bool Empty() { return stk[0].Empty() && stk[1].Empty(); }
void Print() {
for (int i = 0; i <= 10; i ++)
cout << Query(i) << ' '; cout << endl;
return;
}
};
void slv() {
Read(m);
int cur = 0; id[n ++] = cur ++;
upd[0] = mkt(-1, -1, -1);
for (int i = 0; i < m; i ++) {
int opt, u; Read(opt, u), u --;
if (opt == 1) {
id[n ++] = cur;
G[id[u]].emplace_back(cur);
upd[cur] = mkt(-1, -1, -1), cur ++;
} else if (opt == 2) {
int p, t; Read(p, t);
G[id[u]].emplace_back(cur);
upd[id[u] = cur] = mkt(0, p, t), cur ++;
} else if (opt == 3) {
G[id[u]].emplace_back(cur);
upd[id[u] = cur] = mkt(1, 0, 0), cur ++;
} else {
int p = Read<int>();
qry[id[u]].emplace_back(p, q ++);
}
}
Deque Q;
auto DFS = [&](auto self, int u) -> void {
auto [o, p, t] = upd[u];
if (o == 0) Q.Push_Back(mkp(p, t));
if (o == 1) tie(p, t) = Q.Front(), Q.Pop_Front();
for (auto [x, id] : qry[u]) ans[id] = Q.Query(x);
for (auto v : G[u]) self(self, v);
if (o == 0) Q.Pop_Back();
if (o == 1) Q.Push_Front(mkp(p, t));
return;
}; DFS(DFS, 0);
for (int i = 0; i < q; i ++) Write(ans[i], '\n');
return;
}