結果
| 問題 |
No.1951 消えたAGCT(2)
|
| コンテスト | |
| ユーザー |
Jikky1618
|
| 提出日時 | 2025-02-12 00:30:59 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 232 ms / 3,000 ms |
| コード長 | 8,377 bytes |
| コンパイル時間 | 3,074 ms |
| コンパイル使用メモリ | 284,432 KB |
| 実行使用メモリ | 29,276 KB |
| 最終ジャッジ日時 | 2025-02-12 00:31:09 |
| 合計ジャッジ時間 | 6,230 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 28 |
ソースコード
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#ifdef LOCAL
#include <debug.hpp>
#else
#define debug(...)
#endif
template <class S, S (*op)(S, S), S (*e)()>
class SplayTree {
private:
struct Node {
S val, sum; // 自身の値と部分木全体の集約値
int size; // 部分木のサイズ
bool rev; // 遅延反転フラグ
Node *lch, *rch, *par; // 左子、右子、親へのポインタ
Node() : val(e()), sum(e()), size(0), rev(false), lch(nullptr), rch(nullptr), par(nullptr) {}
Node(const S& v) : val(v), sum(v), size(1), rev(false), lch(nullptr), rch(nullptr), par(nullptr) {}
};
Node* root;
inline int size(Node* t) const { return t ? t->size : 0; }
inline S sum(Node* t) const { return t ? t->sum : e(); }
// t のサイズ・集約値を左右の子から更新
inline void push_up(Node* t) {
if (!t) return;
t->size = 1;
t->sum = t->val;
if (t->lch) {
t->size += t->lch->size;
t->sum = op(t->lch->sum, t->sum);
}
if (t->rch) {
t->size += t->rch->size;
t->sum = op(t->sum, t->rch->sum);
}
}
// 反転フラグが立っていたら子に伝搬し、左右の子を入れ替える
inline void push_down(Node* t) {
if (t && t->rev) {
swap(t->lch, t->rch);
if (t->lch) t->lch->rev ^= true;
if (t->rch) t->rch->rev ^= true;
t->rev = false;
}
}
// x とその親 p を回転する(x を上げる)
void rotate(Node* x) {
Node *p = x->par, *g = p->par;
push_down(p);
push_down(x);
if (x == p->lch) {
p->lch = x->rch;
if (x->rch) x->rch->par = p;
x->rch = p;
} else {
p->rch = x->lch;
if (x->lch) x->lch->par = p;
x->lch = p;
}
x->par = g;
if (g) {
if (p == g->lch)
g->lch = x;
else
g->rch = x;
}
p->par = x;
push_up(p);
push_up(x);
}
// k 番目 (0 <= t < size) のノードを根に持ってくる
Node* kth_element(Node* t, int k) {
while (true) {
push_down(t);
int lsize = size(t->lch);
if (k == lsize) break;
if (k < lsize) {
t = t->lch;
} else if (k > lsize) {
k -= lsize + 1;
t = t->rch;
}
}
splay(t);
return t;
}
// x を根に持ってくる
void splay(Node* x) {
if (!x) return;
while (x->par) {
Node* p = x->par;
Node* g = p->par;
if (g) push_down(g);
push_down(p);
push_down(x);
if (!g) {
rotate(x);
} else if ((g->lch == p && p->lch == x) || (g->rch == p && p->rch == x)) {
rotate(p);
rotate(x);
} else {
rotate(x);
rotate(x);
}
}
root = x;
}
// 木 t を先頭 k 個と残りに分割し、ペア {l, t} を返す
pair<Node*, Node*> split(Node* t, int k) {
if (!t) return {nullptr, nullptr};
if (k == 0) return {nullptr, t};
if (k >= size(t)) return {t, nullptr};
// t を k 番目のノードが根になるよう splay する
t = kth_element(t, k);
Node* l = t->lch;
l->par = nullptr;
t->lch = nullptr;
push_up(t);
return {l, t};
}
// l, r を結合する
Node* merge(Node* l, Node* r) {
if (!l) return r;
if (!r) return l;
// l の最大要素(右端)を splay する
l = kth_element(l, size(l) - 1);
l->rch = r;
r->par = l;
push_up(l);
return l;
}
Node* build(const vector<S>& V, int l, int r) {
if (l >= r) return nullptr;
int m = (l + r) >> 1;
Node* t = new Node(V[m]);
t->lch = build(V, l, m);
t->rch = build(V, m + 1, r);
if (t->lch) t->lch->par = t;
if (t->rch) t->rch->par = t;
push_up(t);
return t;
}
void delete_all(Node* t) {
if (!t) return;
delete_all(t->lch);
delete_all(t->rch);
delete t;
}
public:
SplayTree() : root(nullptr) {}
SplayTree(const vector<S>& V) : root(nullptr) {
if (!V.empty()) root = build(V, 0, V.size());
}
~SplayTree() { delete_all(root); }
// get: k (0 <= k < size) 番目の要素の値を返す
S get(int k) {
assert(root && 0 <= k && k < root->size);
return kth_element(root, k)->val;
}
// insert: 位置 k に値 v を挿入する
void insert(int k, S v) {
assert(0 <= k && k <= size(root));
Node* newNode = new Node(v);
if (!root) {
root = newNode;
return;
}
if (k == 0) {
root = merge(newNode, root);
} else if (k == size(root)) {
root = merge(root, newNode);
} else {
auto [L, R] = split(root, k);
root = merge(merge(L, newNode), R);
}
}
// erase: 位置 k の要素を削除する
void erase(int k) {
assert(root && 0 <= k && k < root->size);
Node* target = kth_element(root, k);
Node *L = target->lch, *R = target->rch;
if (L) L->par = nullptr;
if (R) R->par = nullptr;
delete target;
root = merge(L, R);
}
// update: 位置 k の値を v に更新する
void update(int k, S v) {
assert(root && 0 <= k && k < root->size);
Node* target = kth_element(root, k);
target->val = v;
push_up(target);
}
// prod: 区間 [l, r) の要素を op で集約した値を返す
S prod(int l, int r) {
assert(0 <= l && r <= size());
if (l >= r) return e();
auto [A, BC] = split(root, l);
auto [B, C] = split(BC, r - l);
S res = B ? B->sum : e();
root = merge(A, merge(B, C));
return res;
}
// reverse: 区間 [l, r) の要素を反転する
void reverse(int l, int r) {
assert(0 <= l && r <= size());
if (l >= r) return;
auto [A, BC] = split(root, l);
auto [B, C] = split(BC, r - l);
if (B) B->rev ^= true;
root = merge(A, merge(B, C));
}
// rotate: 区間 [l, r) を m 番目の要素が先頭になるように巡回シフトする
// 0 <= l < m < r <= size()
void rotate(int l, int m, int r) {
assert(0 <= l && l < m && m < r && r <= size());
auto [A, BC] = split(root, l);
auto [B, C] = split(BC, r - l);
auto [B1, B2] = split(B, m - l); // B = [B1, B2] で、B2 が先頭に来る
B = merge(B2, B1);
root = merge(A, merge(B, C));
}
bool empty() const { return root == nullptr; }
int size() const { return size(root); }
S operator[](int k) { return get(k); }
};
using T = int;
constexpr T e() { return 0; }
T op(T a, T b) { return a + b; }
int main() {
cin.tie(nullptr);
ios::sync_with_stdio(false);
cout << fixed << setprecision(20);
int N;
cin >> N;
string S;
cin >> S;
constexpr int z = 26;
// A, B, C, ..., Z を 0, 1, 2, ..., 25 に変換
vector<int> V(N);
for (int i = 0; i < N; i++) V[i] = S[i] - 'A';
SplayTree<T, op, e> st(V);
// cur := 0 の箇所
// cnt[c] := 文字 c - cur がいくつあるか
int cur = 0;
vector<int> cnt(z);
for (int i = 0; i < N; i++) cnt[V[i]]++;
// c mod z
auto f = [&](int c) -> int { return (c % z + z) % z; };
int ans = 0;
while (true) {
// c1 := A, G, C, T の個数
int c1 = cnt[f(0 - cur)] + cnt[f(6 - cur)] + cnt[f(2 - cur)] + cnt[f(19 - cur)];
if (c1 == 0) break;
// c1 番目の文字 c を削除
int c = st[c1 - 1];
st.erase(c1 - 1);
cnt[c]--;
// c2 := 文字 c の個数
int c2 = cnt[c];
// c2 回, 文字を次の文字に置き換える
cur = f(cur + c2);
ans++;
}
cout << ans << '\n';
}
Jikky1618