ABC 247
Table of Contents
A - Move Right
$0 s_0 s_1 s_2$ を出力すればよい
void solve() {
string s;
cin >> s;
cout << 0 << s.substr(0, 3) << endl;
}
B - Unique Nicknames
人 $i$ について, 次の条件 $P$, $Q$ のうちいずれかが成り立っていればその人にあだ名をつけることは可能である.
- P: $s_i$ をニックネームとして選んだとき, 全ての $i \neq j$ に対して $s_i \neq s_j \wedge s_i \neq t_i$
- Q: $t_i$ をニックネームとして選んだとき, 全ての $i \neq j$ に対して $s_i \neq s_j \wedge s_i \neq t_i$
これが全ての人に対して調べる.
void solve() {
int n;
cin >> n;
vector<string> S(n), T(n);
rep(i, n) cin >> S[i] >> T[i];
rep(i, n) {
int ok = 1;
rep(j, n) {
if (i == j)
continue;
if (S[i] == S[j] || S[i] == T[j])
ok = 0;
}
if (ok)
continue;
ok = 1;
rep(j, n) {
if (i == j)
continue;
if (T[i] == S[j] || T[i] == T[j])
ok = 0;
}
if (!ok) {
cout << "No" << endl;
return;
}
}
cout << "Yes" << endl;
}
C - 1 2 1 3 1 2 1
$S_i$ の長さを $L_i$ とすると $L_i = 2L_{i - 1} + 1$ となる. $i = 16$ のときでもおおよそ長さは $2^i$ くらい. $1 \sim N$ まで全ての要素数を列挙すると $\sum_{i=0}^N 2^i = O(2^N)$ となるので $N = 16$ ならば $2^16 = 65536$ なので TLE も MLE も特に気にしなくて良い.
$S_i$ の配列は $S_{i-1} + i + S_{i-1}$ を地道に構成すれば良い.
void solve() {
int n;
cin >> n;
vvint S(n + 1);
S[1].push_back(1);
rep2(i, 2, n + 1) {
S[i].insert(S[i].end(), all(S[i - 1]));
S[i].push_back(i);
S[i].insert(S[i].end(), all(S[i - 1]));
}
print(S[n]);
}
D - Cylinder
deque に数字 $x$ が $c$ 個連続しているという情報を $(x, c)$ として保存していく.
- クエリ1 のとき: deque の末尾の要素が $(X, C)$ とする
- $X = x$ のとき, 末尾の要素を削除して, $(X, C+c)$ を新たに末尾に追加する
- $X \neq x$ のとき, 末尾に $(x, c)$ を追加する
- クエリ2 のとき: deque の先頭から条件を満たすように要素を抜き出して和を出力する
void solve() {
int q;
cin >> q;
deque<P> deq;
rep(i, q) {
int t;
cin >> t;
if (t == 1) {
ll x, c;
cin >> x >> c;
if (deq.empty()) {
deq.push_back({x, c});
continue;
}
auto [X, C] = deq.back();
deq.pop_back();
if (X == x) {
deq.push_back({x, c + C});
} else {
deq.push_back({X, C});
deq.push_back({x, c});
}
} else {
ll ans = 0;
ll c;
cin >> c;
while (c) {
auto [X, C] = deq.front();
deq.pop_front();
ll mn = min(c, C);
ans += X * mn;
C -= mn;
c -= mn;
if (C != 0)
deq.push_front({X, C});
}
cout << ans << endl;
}
}
}
E - Max Min
$L$ を固定して, 条件を満たす $R$ を探すことを考える.
最小値が $Y$ となる一番右端の index を $R_i$, 最大値が $X$ となる一番右端の index を $R_j$ とすると $R$ の最大値は $R_{\mathrm{max}} = \min(R_i, R_j)$ となる.
最小値が $Y$ となる一番左端の index を $R_m$, 最大値が $X$ となる一番左端の index を $R_n$ とすると $R$ の最小値は $R_{\mathrm{min}} = \max(R_m, R_n)$ となる.
$R \in [ R_{\mathrm{min}}, R_{\mathrm{max}} ]$ のとき条件を満たすから $L$ を固定したときの $R$ の 場合の数は $R_{\mathrm{max}} - R_{\mathrm{min}} + 1$ となる.
問題は $R_{\mathrm{max}}$, $R_{\mathrm{min}}$ をどうやって高速に求めるかだが, これは segment tree で二部探索すると高速に求めることができる.
ll opmin(ll a, ll b) {
return min(a, b);
}
ll emin() {
return (ll)(1e9);
}
ll opmax(ll a, ll b) {
return max(a, b);
}
ll emax() {
return -1;
}
ll target_max;
ll target_min;
bool fmax(ll v) {
return v <= target_max;
}
bool fmin(ll v) {
return v >= target_min;
}
bool gmax(ll v) {
return v < target_max;
}
bool gmin(ll v) {
return v > target_min;
}
void solve() {
ll N, X, Y;
cin >> N >> X >> Y;
vll A(N);
rep(i, N) cin >> A[i];
target_max = X;
target_min = Y;
segtree<ll, opmin, emin> minseg(A);
segtree<ll, opmax, emax> maxseg(A);
ll ans = 0;
rep(i, N) {
// 区間最大値が X, 区間最小値が Y となるような i 以上の最大の座標
ll x = min(minseg.max_right<fmin>(i), maxseg.max_right<fmax>(i));
// 区間最大値が X, 区間最小値が Y となるような i 以上の最小の座標
ll y = max(minseg.max_right<gmin>(i), maxseg.max_right<gmax>(i));
if (x > y)
ans += x - y;
}
cout << ans << endl;
}