競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 290E

数え上げの順序を変える. \(a_{l} \neq a_{r}\) かつ \(l < r\) となる \(l,r \in N\) に対して,これを含む連続した区間を数える. それは, \(min(l+1, N-r)\) と一致する. minを外すために,大小で場合分けすることと, \(l,r\) のうち \(l\) を全探索して \(r\) を定数をみなすとよい.
数えるべきは, \(r \in N\) かつ \(a_{l} \neq a_{r}\) かつ \(l < r \leq N-l-1\) となる \(r\) の個数. \(\neq\) は調べづらいので, \(=\) の個数を数えて全体から引く 方針にする. これは, \(cnt_{x} := \) (i \(in N\) であって, \(a_{i} = x\)となるもの全体 ) を用意して binary search で求まる.

実装
大小の場合分けは, \(a\) を反転させればコードを使いまわせて安全. ただし, \(l+1 = N-r\) の場合だけ重複して数えているので, それは引く.
注意:区間が空になるときは0を足すことになる. 区間 \([x,y)\) だからといって \(f(y) - f(x)\) の形にしてしまうと, 空の区間で失敗するので if 文で分ける.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n, m ;
  cin >> n;
  vll a(n); rep(i,n) { cin >> a[i]; a[i]--; }

  ll ans = 0;

  rep(l,n){
    ll nl = n - l -1;
    if(l > nl) continue; // empty segment
    if(a[nl] != a[l]) ans -= l+1;
  }

  rep(_,2){
    vvll pos(n);
    rep(i,n){
      pos[a[i]].push_back(i);
    }
    rep(l,n){
      ll nl = n-l-1;
      if(l > nl) continue; // empty segment
      ans += (nl - l)*(l+1);
      auto it0 = lower_bound(all(pos[a[l]]), l+1);
      auto it1 = lower_bound(all(pos[a[l]]), nl+1);
      ans -= (it1 - it0)*(l+1);
    }

    reverse(all(a));
  }
  cout << ans << endl;

  return 0;
}