题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=5184
思路
这道题看上去是个搜索或者DP的题目,但是实际上题目只需要 O(1) 时间便能求出答案,这道题是由一道很经典的类卡特兰数的问题改编而来。原题是这样的:
n+m
个人排队买票,并且满足,票价为50元,其中
n
个人各手持一张50元钞票,
上题相当于限制了所有拿50元的人必须和拿100的人达到两两配对。可能还会多出一部分拿100元的人。最终其答案为 (n+mn)−(n+mn+1)
而在这个题中,由于已经有一部分括号已经被确定了,那么这部分括号我们无视掉,此时还剩下一些左括号和右括号,左括号相当于拿50元的人,右括号相当于拿100元的人,而且可能拿100的人比拿50的人多(右括号个数保证大于等于左括号)。那么这个题就和上面那道题一样了。
然后注意要判定无解的情况
代码
1、扩展欧几里得在线求逆元
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100000
#define MOD 1000000007
using namespace std;
typedef long long int LL;
LL facto[MAXN]; //facto[i]=i!
char s[MAXN];
inline LL extGCD(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
LL gcd=extGCD(b,a%b,x,y);
LL t=x;
x=y;
y=t-(a/b)*y;
return gcd;
}
inline LL rev(LL t) //求t的逆元
{
LL x,y;
extGCD(t,MOD,x,y);
x=(x%MOD+MOD)%MOD;
return x;
}
inline LL C(int a,int b) //C(a,b)
{
return (facto[a]*rev(facto[b]*facto[a-b]%MOD))%MOD;
}
int main()
{
facto[0]=facto[1]=1;
for(int i=2;i<MAXN;i++) facto[i]=(facto[i-1]*i)%MOD;
int n;
while(scanf("%d",&n)!=EOF)
{
bool flag=true;
//memset(s,0,sizeof(s));
int sum=0; //sum=必须匹配的右括号长度
scanf("%s",s+1);
int m=strlen(s+1); //已经确定的括号长度
for(int i=1;i<=m;i++)
{
if(s[i]=='(') sum++;
else sum--;
if(sum<0) flag=false;
}
if(!flag||n&1) //无解
{
printf("0\n");
continue;
}
printf("%lld\n",((C(n-m,(n-m-sum)/2)-C(n-m,(n-m-sum)/2-1)+MOD)%MOD));//%MOD+MOD)%MOD);
}
return 0;
}
2、线性筛法离线求逆元
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100000
#define MOD 1000000007
using namespace std;
typedef long long int LL;
LL facto[MAXN],frac[MAXN]; //facto[i]=i!,frac[i]=1/(i!)
char s[MAXN];
LL rev[MAXN];
inline LL C(int a,int b) //C(a,b)
{
return (((facto[a]*frac[b])%MOD)*frac[a-b])%MOD;
}
int main()
{
rev[1]=1;
for(int i=2;i<MAXN;i++)
{
rev[i]=(MOD-MOD/i)*rev[MOD%i]%MOD;
}
facto[0]=facto[1]=frac[0]=frac[1]=1;
for(int i=2;i<MAXN;i++) facto[i]=(facto[i-1]*i)%MOD;
for(int i=2;i<MAXN;i++) frac[i]=(frac[i-1]*rev[i])%MOD;
int n;
while(scanf("%d",&n)!=EOF)
{
bool flag=true;
//memset(s,0,sizeof(s));
int sum=0; //sum=必须匹配的右括号长度
scanf("%s",s+1);
int m=strlen(s+1); //已经确定的括号长度
for(int i=1;i<=m;i++)
{
if(s[i]=='(') sum++;
else sum--;
if(sum<0) flag=false;
}
if(!flag||n&1) //无解
{
printf("0\n");
continue;
}
printf("%lld\n",((C(n-m,(n-m-sum)/2)-C(n-m,(n-m-sum)/2-1)+MOD)%MOD));//%MOD+MOD)%MOD);
}
return 0;
}