少ない学びをせめて記録する

技術記録、競プロメモ、その他調べたことを書く @京都, twitter : @nehan_der_thal

遅延セグメントツリーをC#で動かす

先日のEducational Codeforces Contest でE問題が遅延セグメントツリーを利用する必要があったのですがpypyでは通らなかったのでC#で書き直してACを得ました。 せっかくなので実装を残しておきます。 非再帰版の遅延評価セグメント木の実装メモ - 日々drdrする人のメモ の書き方が簡潔だったのでほぼ丸写しです。

using System;
using System.Linq;
using System.Collections.Generic
using static System.Math;

class SegTree
{
  public long[] data;
  public long[] lazydata;
  private int num;
  private long ide_ele;
  private long F(long x, long y){
    if (x>y) return x;
    return y;
  }
  private int Pow(int x, int y){
    int r = 1;
    for (int i = 0; i < y; i++){
      r *= x;
    }
    return r;
  }
  private List<int> Gindices(int l, int r){
    List<int> R = new List<int>();
    l += num; r += num;
    int lm = (l / (l & -l))>>1;
    int rm = (r / (r & -r))>>1;
    //Console.WriteLine(rm);
    while (l < r){
      if (r <= rm){
        R.Add(r-1);
      }
      if (l <= lm){
        R.Add(l-1);
      }
      l >>= 1; r >>= 1;
    }
    while (l > 0){
      R.Add(l-1);
      l >>= 1;
    }
    return R;
  }

  private void Propagate(List<int> indices){
    foreach(int i in indices.AsEnumerable().Reverse()){
      long v = lazydata[i]; lazydata[i] = 0;
      if (v==0) continue;
      lazydata[2*i+1] += v; data[2*i+1] += v;
      lazydata[2*i+2] += v; data[2*i+2] += v;
    }
  }

  public void Update(int i, long x){
    i += num-1;
    data[i] = x;
    while (i>0){
      i = (i-1)>>1;
      data[i] = F(data[2*i+1], data[2*i+2]);
    }
  }
  public void UpdateRange(int l, int r, long x){
    //Console.WriteLine($"{l}, {r}");
    List<int> gindices = Gindices(l, r);
    l += num; r += num;
    while (l < r){
      if (l%2 == 1){
        lazydata[l-1] += x; data[l-1] += x; l++;
      }
      if (r%2 == 1){
        r--; lazydata[r-1] += x; data[r-1] += x;
      }
      l >>= 1; r >>= 1;
    }
    foreach(int i in gindices){
      //Console.WriteLine(i);
      data[i] = F(data[2*i+1], data[2*i+2]) + lazydata[i];
    }
  }
  public long Query(int l, int r){
    Propagate(Gindices(l, r));
    l += num; r += num;
    long R = ide_ele;
    while (l < r){
      //Console.WriteLine($"Q {l-1} {r-2}");
      if (l%2 == 1){
        R = F(R, data[l-1]); l++;
      }
      if (r%2 == 1){
        r--; R = F(R, data[r-1]);
      }
      l >>= 1; r >>= 1;
    }
    return R;
  }
  public SegTree(long[] values, long ide_ele)
  {
    int N = values.Length;
    this.ide_ele = ide_ele;
    int tmp = N+1; // 遅延操作のため1段多く作る
    int r = 0;
    while (tmp > 0){
      tmp >>= 1;
      r++;
    }
    num = Pow(2, r);
    data = new long[2*num];
    lazydata = new long[2*num];
    for (int i = 0; i < N; i++){
      data[i+num-1] = values[i];
    }
    for (int i = num - 2; i >= 0; i--){
      data[i] = F(data[2*i+1], data[2*i+2]);
    }
  }
}

class P
{
  static void Main()
  {
    SegTree x = new SegTree(new long[] {12, 32, 21, 24}, -long.MaxValue);
    x.Update(0, 53);
    x.UpdateRange(2, 3, 8);
    x.UpdateRange(2, 4, 5);
    Console.WriteLine(x.Query(1, 4));
  }
}

利用例 :

https://codeforces.com/contest/1295/submission/69992196

問題は抽象化で現在、各値はlongで持つことが前提の実装になっています。 色々な型を入れられるようになりたいな。C#の習熟度が足りないのでまた次回に。