Codeforces Round #404 (Div.2) D. Anton and School - 2

解法

条件に合う部分列の,一番右端の "(" の位置を総当りして計算します.
その位置から左に(その位置自体も含めて)"(" が $x$ 個あり,右に ")" が $y$ 個ある時,求める答えは $\displaystyle \sum_{i = 1}^{\min (x, y)}({_{x-1}C_{i-1}} \times {_yC_i})$ となります.簡単のため $x < y$ の時を考えます.
右辺の各 i についての式 $_{x-1}C_{i-1} \times _yC_i$ が意味するものを考えます.式変形して,$_{x-1}C_{x-i} \times _yC_i$ としておきます.
これは $x - 1$ 人からなるグループから $x - i$ 人を選び,$y$ 人からなるグループから $i$ 人を選ぶ組み合わせに対応します.つまり,$x - 1 + y$ 人から $x$ 人を選ぶ方法のうち,$x - 1$ 人の方から $x - i$ 人を選ぶときの組み合わせに対応します.
すると,$i$ を $1$ から $x$ まで動かした時,$x - 1 + y$ 人から $x$ 人を選ぶ方法すべてを網羅することになります.よって,
$\displaystyle \sum_{i = 1}^{x}({_{x-1}C_{i-1}} \times {_yC_i}) = _{x+y-1}C_x$
が成り立ち,求めたい値を O(1) で求めることができます.(前計算は必要です.)

・おまけ
ちょっと一般化すると
$\displaystyle \sum_{i=0}^z{_xC_i}{_yC_{z-i}} = _{x+y}C_{z}~ (z \leq x)$
で,$z = x$ とすれば
$\displaystyle \sum_{i=0}^x{_xC_i}{_yC_i} = _{x+y}C_{x}$

ソースコード

#include <bits/stdc++.h>
using namespace std;

using ll = long long;

// modulo は略

const int MOD = 1000000007;
using mod = modulo<MOD, true>;

int main() {
    string s;
    cin >> s;
    int n = s.size();
    vector<int> left(n + 1), right(n + 1);
    for(int i = 0; i < n; ++i) {
        left[i + 1] = left[i] + (s[i] == '(');
        right[n - i - 1] = right[n - i] + (s[n - i - 1] == ')');
    }
    mod res = 0;
    for(int i = 1; i <= n; ++i) {
        if(s[i - 1] != '(') {
            continue;
        }
        if(right[i] != 0) {
            res += comb<MOD>(left[i] + right[i] - 1, left[i]);
        }
    }
    cout << (ll)res << endl;
}

感想

式はすぐ出て来るが,変形で詰まった.