@ArrowLLL
2017-04-06T10:55:07.000000Z
字数 1645
阅读 1794
组合数学 算法
主页地址 :月光森林
仍然任性地践行不写题解的原则,于是默默地把标题改成了组合数学例题。但是实际上还是一篇题解,大家就默默地看不要吐槽就好。
OK, link start:
先给出原题链接 : codeforces#Round404(div-2)-D
一个RSBS(标准简式括号表达式)是指一个只包含'(' 和 ')' 的长度为n的字符串,且满足一下条件:
现在给定一个长度为 的括号表达式 s ,问 s 中有多少个子串是 RSBS。
所谓子串是指删掉 s 中的一些字符得到的串。所以题目实际上是在问有多少种删除字符的方式可以获得一个RSBS, 或者说是从原串 s 中选择出一定字符可以组成一个RSBS的不同方法。
假如 s 串中一共有 m 个左括号,则可以将从 s 中得到的RSBS作 m 个划分, 每个划分是以第 i ()个左括号为中心得到的RSBS。
因此,可以以左括号("(")的位置作为枚举对象。
假设对于某个位置处的左括号,它左边有 个左括号,右边有 个右括号, 则对于这个确定的左括号来说, 以它为中心的得到的RSBS的数量满足:
对于这个公式,实际上可以化简 ,直接上结论:
这相当于连续摆放的 个物品,从中选取 个物品。
这个题目的难点就在于化简,比赛当时想到了这个思路和公式,但就是想不出来怎么化简。比赛结束看讨论才知道的:
而如何计算组合数,可以从本人之前的博客查看,根据数据范围,这个地方使用的是公式法+逆元。
#include <bits/stdc++.h>using namespace std;typedef long long LL;const LL maxn(2e5 + 5);char bct[maxn];int r[maxn], n; // r[]数组记录右括号的数量/******** 计算C(n, m) 的算法********/const LL mod(1e9 + 7);LL jC[maxn];LL niY(LL t){LL p = mod - 2, res = 1;while(p){if(p & 1) res = res * t % mod;t = t * t % mod;p >>= 1;}return res;}void init(){jC[0] = jC[1] = 1;for(int i = 2; i < maxn; i++)jC[i] = jC[i - 1] * i % mod;}LL calC(LL n, LL m){LL ma = niY(jC[m]) * niY(jC[n - m]) % mod;return (jC[n] * ma % mod) % mod;}/******** 计算C(n, m) 的算法********/int main(){init();//freopen("data.in", "r", stdin);while(~scanf("%s", bct)){n = strlen(bct);r[n] = 0;for(int i = n - 1; i >= 0; i--)r[i] = bct[i] == ')' ? r[i + 1] + 1 : r[i + 1];LL ans = 0;for(int i = 0, j = 0; i < n; i++)if(bct[i] == '('){ans = ans + calC(j + r[i], r[i] - 1);ans %= mod, j ++;}printf("%I64d\n", ans);}return 0;}
以上です~