068 - Paired Information(★5) Editorial by ngtkana
クエリ先読みも union-find も使わず、セグメント木を用いてクエリをオンラインに処理する、\(O (N + Q \ln N)\) 時間の方法をご紹介します。
まず \(N - 1\) 個の項からなる数列 \(( V _ i ) _ { i = 1 } ^ { N - 1 }\) と定数 \(x\) に対し、\(N\) 個の不定元 \(( A _ i ) _ { i = 1 } ^ N\) を持つ連立方程式
\[ \begin{aligned} A _ 1 + A _ 2 &= V _ 1, \\ A _ 2 + A _ 3 &= V _ 2, \\ &\vdots \\ A _ { N - 1 } + A _ N &= V _ { N - 1 }, \\ A _ i &= x \\ \end{aligned} \]
が一意な解
\[ \begin{aligned} A _ j &= \mathtt { alt } (i. j) - x &\ & (j -i \in 2 \mathbb Z + 1), \\ A _ j &= x \mp \mathtt { alt } (i. j) &\ & (j -i \in \pm 2 \mathbb N) \\ \end{aligned} \]
(ただし、\(\mathtt { alt } (i. j) \) はインデックス半開区間 \(\lbrack i, j\lbrack\) に対応する交代和 \(\sum _ { k = i } ^ { j - 1 } ( - 1 ) ^ { k - i } V _ k \ ( i \le j)\) です。)を持つことに注意します。
すると、クエリに答えるためには
- \(V\) の特定のインデックス区間 \(\lbrack i, j \lbrack\) に判明していない要素があればその旨を報告し、
- さもなくば交代和 \(\mathtt { alt } ( i, j)\) を計算する
という機能を持つデータ構造があればよいことがわかります。それは次のようなセグメント木で実現できます。
- 値:
- \(V\) の値 \(x\) が判明している箇所は \((x, 1)\)
- 判明していない場合は \((0, 0)\)
- 演算:
- \((x, l) \circ (y, m) = (x + (-1) ^ l y, l + m)\)
なお、結果の第 \(2\) 要素が区間の長さと一致するとき、またそのときに限りその区間の \(V\) の値がすべて判明しており、さらにそのとき第 \(1\) 要素が交代和になっています。
use proconio::{fastout, input, marker::Usize1};
#[fastout]
fn main() {
input! {
n: usize,
q: usize,
queries: [(u8, Usize1, Usize1, i64); q],
}
let mut seg = Segtree(vec![(0, 0); 2 * n]);
for &(com, i, j, value) in &queries {
if com == 0 {
if seg.sum(i, i + 1).1 == 0 {
seg.set(i, value);
}
} else {
let (alt, len) = seg.sum(i.min(j), i.max(j));
if len == i.max(j) - i.min(j) {
let ans = if len % 2 == 1 {
alt - value
} else {
value + if i < j { -alt } else { alt }
};
println!("{}", ans);
} else {
println!("Ambiguous");
}
}
}
}
struct Segtree(Vec<(i64, usize)>);
impl Segtree {
fn set(&mut self, mut i: usize, value: i64) {
i += self.0.len() / 2;
self.0[i] = (value, 1);
while i != 1 {
i /= 2;
self.0[i] = add(self.0[2 * i], self.0[2 * i + 1]);
}
}
fn sum(&self, l: usize, r: usize) -> (i64, usize) {
let mut lsum = (0, 0);
let mut rsum = (0, 0);
let mut l = l + self.0.len() / 2;
let mut r = r + self.0.len() / 2;
while l < r {
if l % 2 == 1 {
lsum = add(lsum, self.0[l]);
l += 1;
}
if r % 2 == 1 {
r -= 1;
rsum = add(self.0[r], rsum);
}
l /= 2;
r /= 2;
}
add(lsum, rsum)
}
}
fn add(lhs: (i64, usize), rhs: (i64, usize)) -> (i64, usize) {
(
lhs.0 + if lhs.1 % 2 == 0 { rhs.0 } else { -rhs.0 },
lhs.1 + rhs.1,
)
}
提出 (40 ms): https://atcoder.jp/contests/typical90/submissions/28338771
posted:
last update: