题目
Problem Description
对于一个串S,当它同时满足如下条件时,它就是一个01偏串:
1、只由0和1两种符组成;
2、在S的每一个前缀中,0的个数不超过1的个数;
3、S中0的个数和1的个数相等。
现在给定01偏串S,请计算一下S在所有长度为n的01偏串中作为子串出现的次数的总和。 由于结果比较大,结果对1e9+7取余后输出。
Sample Input
2
2 10
4 10
Sample Output
1
3
样例解释:
在第二个样例中,长度为4的偏串共两个1010,1100。10在1010中出现了两次,在1100中出现了1次。所以答案是3。
Input
第一行给出一个整数T(1<=T<=40),表示测试数据的数目。 每一组测试包含一个整数n和字符串S,中间用空格分开。(1<=|S|<=100000,1<=n<=1000000000)
输入保证S是一个01偏串。
Output
对于每一组数据,输出一个整数占一行,表示答案。
题目大意:
对于一个串S,当它同时满足如下条件时,它就是一个01偏串:
- 只由0和1两种符组成;
- 在S的每一个前缀中,0的个数不超过1的个数;
- S中0的个数和1的个数相等。
现在给定01偏串S,请计算一下S在所有长度为n的01偏串中作为子串出现的次数的总和。
由于结果比较大,结果对 1 0 9 + 7 10^9+7 109+7取余后输出。
解析
打表?暴力?额,好像不会做。
其实01偏串实际上等价于合法括号序列,在合法括号序列中取出一个合法括号序列之后剩下的一定也是一个合法括号序列
所以我们计算
n
−
l
e
n
S
n-lenS
n−lenS的合法括号序列数,乘上
S
S
S在
n
n
n中的位置种类即可,计算卡特兰数时候因为模比较大,我们采用分段打表。注意n-s<0||(n-s)%2==1条件的特判。
然后一开始打
s
q
r
t
(
n
)
sqrt(n)
sqrt(n)的表,代码长度超限
打
1
0
5
10^5
105还是超。。
于是
1
0
6
10^6
106终于过了
本来要是还不过就不会了。。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const LL MOD=1000000007,blk=500000;
const LL lst[2001]={……};
int T,n,s;
char ss[1000010];
LL Fact(LL x){
if(x<0)return 0;
LL res=lst[x/blk];
for(LL i=(x/blk)*blk+1;i<=x;i++)res=(res*i)%MOD;
return res;
}
LL power(LL a,LL b){
if(b==0)return 1;
if(b&1)return(power(a,b-1)*a)%MOD;
LL t=power(a,b/2);return (t*t)%MOD;
}
LL C(LL a,LL b){
if(a<0||b<0||a<b)return 0;
return(Fact(a)*power(Fact(a-b),MOD-2)%MOD*power(Fact(b),MOD-2))%MOD;
}
LL Calc(int x){return (C(2*x,x)-C(2*x,x-1)+MOD)%MOD;}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d %s",&n,ss);
s=strlen(ss);
if(n-s<0||(n-s)%2==1)puts("0");
else printf("%d\n",Calc((n-s)/2)*(n-s+1)%MOD);
}return 0;
}