1. 程式人生 > >卡特蘭數 相關問題 hdu 5184 Brackets

卡特蘭數 相關問題 hdu 5184 Brackets

題解:
當n為奇數的時候答案是0。
先判斷字串的前面是否符合括號匹配,即對於任何字首左括號個數>=右括號個數。
設左括號個數為a右括號個數為b, m=n/2,問題可以轉化為在平面中從座標(a,b)沿網格走到(m,m) 且不跨過x=y這一條直線的方法數。資料太大,普通DP和搜尋都不行的。
問題可以進一步轉化為從(a-n,b-n)到(0,0)且不跨過x=y的方法數。再對稱一下,轉化到(0,0)到(n-b,n-a)不跨過x=y的方法數。

對於從(0,0)點走到(p,q)點不跨過x=y的方法數是
pq+1p+1Cqp+q證明如下: 我們可以通過總的數目來減掉非法的數目即可。 把(0,0)和(p,q)都往下移一格,非法數目即為(0,-1)到(p,q-1)且路徑中至少有一點和x=y相交的方法數。記(d,d)為從(0,-1)到(p,q-1)路徑中最先和x=y相交的點。則由於對稱性(-1,0)到(d,d)的方法數和(0,-1)到(d,d)的方法數是相同的。所以(0,-1)到(p,q-1)且與x=y相交的方法數和(-1,0)到(p,q-1)的方法數是相同的。 所以答案是C
qp+q
Cq1p+q=pq+1p+1Cqp+q
然後對100W以內的數字進行一個階乘處理,就可以O(1)得出答案了。

相關問題:

經典的買票問題,:本次足球比賽的門票為50元,而站排買票的球迷有m個人手裡拿著一張面值50元的鈔票,有n個人手裡拿著一張面值100元的鈔票。工作人員事先忘了為售票處準備任何零錢,請問您是否能算出這(m+n)個人共有多少種排隊方式買票,使售票處不至於出現找不開錢的尷尬局面?

問題即p個‘(’,q個‘)’,p>=q,滿足條件的排列數,答案即為Cqp+qCq1p+q=pq+1p+1Cqp+q

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<utility>
#include<queue>
#include<set>
#include<map>
#include<math.h>
#include<string>
using namespace std;
#define inf 0x3f3f3f3f
#pragma warning(disable:4996)
#pragma comment(linker, "/STACK:102400000,102400000")
#define ll long long
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define mem(a,b) memset(a,b,sizeof(a))
const double eps = 1e-9;
const ll mod = 1e9 + 7;
#define N 1000000

char p[1000010];
ll h[N+10]; //階乘表
ll g[N+10]; //階乘逆元表
ll inv(ll a, ll mod)
{
	return a == 1 ? 1 : (mod - mod / a) * inv(mod % a, mod) % mod;
}
ll C(int a, int b)
{
	if (a < b) return 0;
	return h[a] * g[b] % mod*g[a - b] % mod;
}
void init()
{
	int i;
	h[0] = 1;
	for (i = 1; i <= N; i++)
		h[i] = h[i - 1] * i%mod;
	g[N] = inv(h[N], mod);
	for (i = N; i >= 1; i--)
		g[i - 1] = g[i] * i%mod;
}
int main()
{
	int i, j, k;
	int n;
	init();
	while (scanf("%d", &n) != EOF)
	{
		scanf("%s", p);
		if (n & 1)
		{
			printf("0\n");
			continue;
		}
		int sum = 0;
		int len = strlen(p);
		int flag = 0;
		for (i = 0; i < len; i++)
		{
			if (p[i] == '(')
				sum++;
			else sum--;
			if (sum < 0)
			{
				flag = 1;
				break;
			}
		}
		if (flag == 1)
		{
			printf("0\n");
			continue;
		}
		n -= len;
		if (n == 0)
		{
			if (sum == 0)
				printf("1\n");
			else
				printf("0\n");
			continue;
		}
		if (n < sum)
		{
			printf("0\n");
			continue;
		}
		if (n == sum)
		{
			printf("1\n");
			continue;
		}
		if ((n - sum) % 2 == 1)
		{
			printf("0\n");
			continue;
		}
		int th = (n - sum) / 2;
		int q = th, p = th + sum;
		ll ans = C(p + q, q)*(p - q + 1) % mod*inv(p + 1, mod) % mod;
		printf("%I64d\n", ans);
	}
}