以下の内容はhttps://inamori.hateblo.jp/entry/2026/01/24/191700より取得しました。


AtCoder Beginner Contest 438 G

https://atcoder.jp/contests/abc438/tasks/abc438_g

これも足し算の順番を変えて解きます。順番を変えるというのは、どこを固定するかという話です。ここではBを固定します。入力例1だとまずBの最初の1とそれに対応するAを考えます。Bのindex:i mod Mが0ときにAのindex:i mod Nは0, 2, 1となります。具体的には、3, 4, 1です。このときBは1でこれより小さいAはありません。なので、3*1 = 3となります。個々のBに対応するAで木を作って、Bより小さい値だけ和を取ります。そして、足りない個数ぶんだけBを足します。
次のBも同様に考えます。そうすると、今度はAのindexは1, 0となります。さっきAのindexが0, 2, 1でしたが、最初にMより小さいindexを調べると、0と2を削除して0を追加すればよいです。このように木を少ない計算量で更新できます。
もうちょっと具体的に考えて、N=4, M=3, K=20で考えると、最初のBに対応するAは7つあって、0, 3, 2, 1, 0, 3, 2となります。次にMより小さいのindexは2なので、Bのindex:2に対応するAのindexは2つ削ってトータルの個数は1つ減るから1つ足して、2, 1, 0, 3, 2, 1となります。次のMより小さいindexは1なので、1つ削ってトータルの個数はまた1つ増えるので2つ増やして1, 0, 3, 2, 1, 0, 3となります。
このアルゴリズムはAがBより短いと成り立たないので、そのときはAとBを入れ替えます。

// Sum of Min
#![allow(non_snake_case)]


//////////////////// library ////////////////////

fn read<T: std::str::FromStr>() -> T {
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    line.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>().split_whitespace()
            .map(|e| e.parse().ok().unwrap()).collect()
}

fn gcd(a: usize, b: usize) -> usize {
    if b == 0 { a } else { gcd(b, a%b) }
}


//////////////////// SegTree ////////////////////

trait Monoid {
    type S: Clone;
    fn op(a: &Self::S, b: &Self::S) -> Self::S;
    fn e() -> Self::S;
}

struct SegTree<M: Monoid> {
    n: usize,
    size: usize,
    data: Vec<M::S>,
}

impl<M: Monoid> SegTree<M> {
    fn new(n: usize) -> Self {
        let size = n.next_power_of_two();
        SegTree {
            n,
            size,
            data: vec![M::e(); size*2-1],
        }
    }
    
    fn from(v: Vec<M::S>) -> Self {
        let n = v.len();
        let size = n.next_power_of_two();
        let mut data = vec![M::e(); size*2-1];
        for i in 0..n {
            data[i+size-1] = v[i].clone();
        }
        let mut seg = SegTree { n, size, data };
        for i in (0..size-1).rev() {
            seg.data[i] = M::op(&seg.data[2*i+1], &seg.data[2*i+2]);
        }
        seg
    }
    
    fn set(&mut self, mut i: usize, x: M::S) {
        i = i + self.size - 1;
        self.data[i] = x;
        while i != 0 {
            i = (i - 1) / 2;
            self.data[i] = M::op(&self.data[2*i+1], &self.data[2*i+2]);
        }
    }
    
    fn prod(&self, mut l: usize, mut r: usize) -> M::S {
        let mut sml = M::e();
        let mut smr = M::e();
        l = l + self.size - 1;
        r = r + self.size - 1;
        while l < r {
            if l % 2 == 0 {
                sml = M::op(&sml, &self.data[l]);
                l += 1;
            }
            if r % 2 == 0 {
                r -= 1;
                smr = M::op(&self.data[r], &smr);
            }
            l = (l - 1) / 2;
            r = (r - 1) / 2
        }
        M::op(&sml, &smr)
    }
    
    fn get(&self, i: usize) -> M::S {
        self.data[i+self.size-1].clone()
    }
}


//////////////////// Monoid ////////////////////

struct Sum;
impl Monoid for Sum {
    type S = (usize, usize);    // (個数, 和)
    fn op((a1, b1): &Self::S, (a2, b2): &Self::S) -> Self::S {
        ((a1+a2)%D, (b1+b2)%D)
    }
    fn e() -> Self::S { (0, 0) }
}


//////////////////// Tree ////////////////////

const D: usize = 998244353;

struct Tree {
    seg: SegTree::<Sum>,
    ids: Vec<usize>,
    A: Vec<usize>,
    A_sorted: Vec<usize>
}

impl Tree {
    fn size(&self) -> usize {
        self.seg.size
    }
    
    fn increment(&mut self, i: usize) {
        let id = self.ids[i];
        let (n, v) = self.seg.get(id);
        self.seg.set(id, (n+1, (v+self.A[i]) % D))
    }
    
    fn decrement(&mut self, i: usize) {
        let id = self.ids[i];
        let (n, v) = self.seg.get(id);
        self.seg.set(id, (n-1, (v+D+D-self.A[i]) % D))
    }
    
    // 与えられた値以上のself.A_sortedの最初のindex
    fn first_index(&self, a: usize) -> usize {
        let N = self.A_sorted.len();
        let mut first: usize = 0;
        let mut last: usize = N;
        while last - first > 1 {
            let mid = (first + last) / 2;
            if self.A_sorted[mid] > a {
                last = mid
            }
            else {
                first = mid
            }
        }
        if self.A_sorted[first] >= a {
            first
        }
        else {
            first + 1
        }
    }
    
    // 与えられた値未満の総和
    fn sum(&self, a: usize) -> (usize, usize) {
        let index = self.first_index(a);
        self.seg.prod(0, index)
    }
    
    // Aをソートして重複をなくした配列と
    // Aのidからその配列のidへの配列を作る
    fn make_sorted_id_table(A: &Vec<usize>) -> (Vec<usize>, Vec<usize>) {
        // A:       [7, 7, 3, 5]
        // A_sorted:[3, 5, 7]
        // ids:     [2, 2, 0, 1]
        let N = A.len();
        let mut B: Vec<(usize, usize)> = A.iter().enumerate().
                                            map(|(i, &a)| (a, i)).collect();
        B.sort();
        let mut ids: Vec<usize> = vec![0; N];
        let mut k: usize = 0;
        let mut prev_a: usize = B[0].0;
        let mut C: Vec<usize> = vec![prev_a];
        for &(a, i) in B.iter() {
            if a != prev_a {
                prev_a = a;
                C.push(a);
                k += 1
            }
            ids[i] = k
        }
        (C, ids)
    }
    
    fn create(K: usize, M: usize, A: Vec<usize>) -> Self {
        let N = A.len();
        let (A_sorted, ids) = Tree::make_sorted_id_table(&A);
        let mut seg = SegTree::<Sum>::new(A_sorted.len());
        let Q = (K + M - 1) / M;    // B0が現れる回数
        for i in 0..N {
            let j = i * M % N;
            let q = (Q + N - i - 1) / N;    // Aが現れる回数
            let (n, s) = seg.get(ids[j]);
            seg.set(ids[j], ((n+q)%D, (s+q%D*A[j])%D))
        }
        Tree { seg, ids, A, A_sorted }
    }
}


//////////////////// process ////////////////////

fn read_input() -> (usize, Vec<usize>, Vec<usize>) {
    let v: Vec<usize> = read_vec();
    let K = v[2];
    let A: Vec<usize> = read_vec();
    let B: Vec<usize> = read_vec();
    (K, A, B)
}

fn F_each(K: usize, A: Vec<usize>, B: Vec<usize>) -> usize {
    let N = A.len();
    let M = B.len();
    let q = K / M;
    let r = K % M;
    let mut tree = Tree::create(K, M, A);
    let (n1, s1) = tree.sum(B[0]);
    let n0 = (K + M - 1) / M;
    let mut s = (B[0] * ((n0 - n1) % D) + s1) % D;
    
    let mut j = M % N;  // Aのindex
    let mut jump: usize = 1;
    let mut prev_j = 0;
    while j != 0 {
        if j >= M {
            jump += 1;
            j = (j + M) % N;
            continue
        }
        
        // 同じBがいくつ出てくるか
        let prev_num = if prev_j < r { q+1 } else { q };
        let num = if j < r { q+1 } else { q };
        // 何個追加するか
        let num_additional = jump + num - prev_num;
        // 追加する
        for k in 0..num_additional {
            let j1 = (prev_j+M*(prev_num+k))%N;
            tree.increment(j1)
        }
        
        // 取り除く
        for k in 0..jump {
            let j2 = (prev_j+M*k)%N;
            tree.decrement(j2)
        }
        let (n2, s2) = tree.sum(B[j]);
        s = (s + B[j] * ((num - n2) % D) + s2) % D;
        prev_j = j;
        j = (prev_j + M) % N;
        jump = 1
    }
    s
}

fn F(K: usize, A: Vec<usize>, B: Vec<usize>) -> usize {
    let N = A.len();
    let M = B.len();
    if N < M {
        return F(K, B, A)
    }
    
    let mut s: usize = 0;
    let d = gcd(N, M);
    for i in 0..d {
        let A1: Vec<usize> = (i..N).step_by(d).map(|j| A[j]).collect();
        let B1: Vec<usize> = (i..M).step_by(d).map(|j| B[j]).collect();
        s = (s + F_each((K+d-1-i)/d, A1, B1)) % D
    }
    s
}

fn main() {
    let (K, A, B) = read_input();
    println!("{}", F(K, A, B))
}



以上の内容はhttps://inamori.hateblo.jp/entry/2026/01/24/191700より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14