zoukankan      html  css  js  c++  java
  • 简单的括号序列




    自我感觉是一个签到题,但是想了很久,最后求助才能苟活。

    按照它的定义,很容易想到如何求出合法的序列:

    当我们确定一个右括号时,假设它左边有 (a) 个左括号,右边有 (b) 个右括号,那么我们可以从 (a) 中选出 (i) 个,从 (b) 中选出 (i - 1) 个,即可组成一个合法的序列。

    所以现在答案变成了,对于每个右括号:

    [sum^{ileq min (a-1,b)}_{i=0} inom{a}{i+1} inom{b}{i} ]

    但是我们现在只能是 (Theta(n^2)) 的复杂度,考虑将式子化简。

    式子的本质含义是从 (a) 集合中选 (i+1) 个,从 (b) 集合中选 (i) 个,由:

    [inom{b}{i} = inom{b}{b - i} ]

    可得,问题又转化成了 (a) 集合中选 (i+1) 个,从 (b) 集合中选 (b-i) 个,那直接将 (a,b) 集合合并,直接转化成从 (a+b) 中选 (b+1) 个:

    [inom{a+b}{b+1} ]

    莫得了...

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    typedef long long ll;
    
    using namespace std;
    
    const int maxn = 4e5 + 50, INF = 0x3f3f3f3f, mod = 1e9 + 7;
    
    inline int read () {
    	register int x = 0, w = 1;
    	register char ch = getchar ();
    	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
    	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
    	return x * w;
    }
    
    inline void write (register int x) {
    	if (x / 10) write (x / 10);
    	putchar (x % 10 + '0');
    }
    
    int n, sumf[maxn], sumb[maxn];
    char str[maxn];
    ll ans, fac[maxn], facinv[maxn];
    
    inline ll qpow (register ll a, register ll b) {
    	register ll ans = 1;
    	while (b) {
    		if (b & 1) ans = ans * a % mod;
    		a = a * a % mod, b >>= 1;
    	}
    	return ans;
    }
    
    inline void Init () {
    	fac[0] = 1;
    	for (register int i = 1; i <= 4e5; i ++) fac[i] = fac[i - 1] * i % mod;
    	facinv[400000] = qpow (fac[400000], mod - 2);
    	for (register int i = 4e5; i >= 1; i --) facinv[i - 1] = facinv[i] * i % mod;
    }
    
    inline ll C (register int a, register int b) {
    	if (a == 0 || b == 0 || a == b) return 1;
    	if (a < b) return 0;
    	return fac[a] * facinv[b] % mod * facinv[a - b] % mod;
    } 
    
    int main () {
    	scanf ("%s", str + 1), n = strlen (str + 1), Init ();
    	register int tmp = 0;
    	for (register int i = 1; i <= n; i ++) {
    		if (str[i] == '(') tmp ++;
    		else sumf[i] = tmp;
    	}
    	tmp = 0;
    	for (register int i = n; i >= 1; i --) {
    		if (str[i] == ')') sumb[i] = tmp, tmp ++;
    	}
    	for (register int i = 1; i <= n; i ++) {
    		if (str[i] == '(') continue;
    		ans = (ans + C (sumf[i] + sumb[i], sumb[i] + 1)) % mod;
    	}
    	printf ("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    协方差的意义
    ios7新特性实践
    微信支付大盗--黑色产业链
    UVA 297 Quadtrees(四叉树建树、合并与遍历)
    HDU 2876 Ellipse, again and again
    java中接口的定义与实现
    Oracle Linux 6.3下安装Oracle 11g R2(11.2.0.3)
    Fortran使用隐形DO循环和reshape给一维和多维数组赋初值
    Java实现 蓝桥杯VIP 算法训练 成绩的等级输出
    Java实现 蓝桥杯VIP 算法训练 成绩的等级输出
  • 原文地址:https://www.cnblogs.com/Rubyonly233/p/14077530.html
Copyright © 2011-2022 走看看