Description(translated)
定义明确表达式UAEUAE:
1. 自然数都是UAEUAE(带前导零的也算)——换句话说,数字串是UAEUAE
2. 对于两个UAEUAE:XX与,(X)+or−or∗or/(Y)(X)+or−or∗or/(Y)是UAEUAE
3. 对于一个UAEUAE:XX,与−(X)−(X)都是UAEUAE
给出一个UAEUAE,它的括号都被删掉了,请求出可以满足这个条件的UAEUAE的个数
Input
一行,一个字符串,仅包含+、−、∗、/+、−、∗、/以及数字0到90到9,不超过20002000个字符
Output
一行,一个整数,表示方案数
Sample Input
sample1.in
1+2*3
sample2.in
03+-30+40
sample3.in
5/0
Sample Output
sample1.out
2
sample2.out
3
sample3.out
1
Solution
好神的一道题……
第一,区间DPDP的O(n3)O(n3)不说了,简单
然后,如果没有连续的符号,很显然每个符号都等价了
设符号有aa个,那么,f[1]=1f[1]=1
可以O(n)O(n)求出,当然数据范围小,O(n2)O(n2)也没人打你
现在我们考虑有连续的符号该怎么办
先简单判一下无解
1. 如果∗、/∗、/前面是运算符或者前面没有字符,无解
2. 如果末尾出现运算符,无解
然后就都是有解的了……
考虑一下删去括号之前的UAEUAE,为其添加一些奇怪的东西——在正负号前面添加一组括号。举个栗子——(+(233))−(8)(+(233))−(8)变成(()+(233))−(8)(()+(233))−(8)
由于每个运算符前后都有括号,我们去掉后方括号,上式变成(()+233)−8(()+233)−8,省略数字和运算符就变成了(())(())。
神奇的是,每个运算符号恰好对应一个右括号,运算的顺序和括号的运算顺序也是一样的,不难发现,每个满足条件的UAEUAE恰好对应一个合法括号序列,每个合法括号序列也恰好对应一个满足条件的UAEUAE。但是考虑一下作为正负号的+、−+、−,可以发现它的右括号前面是空的,也就是说必须左右括号紧挨着才可以,写一个组合数学DPDP不是什么难事。
做这道题的时候莫名想起了pruferprufer数列,每一棵有标号无根树都可以转化为唯一的pruferprufer数列,而每一个pruferprufer数列都可以转化为唯一的有标号无根树,而树不容易计数,但pruferprufer数列却很好计数,这样一转化就简单多了。
这题也是一样,把不好计数的UAEUAE转化为比较容易计数的括号序列,好神啊……
PS:PS:我还是太弱了,看了题解还是WA了,忘了特判末尾有运算符的无解情况……
Code:Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 4005
#define mod 1000003
char S[N];
int n,m,v[N],f[N][N];
int main()
{
scanf("%s",S+1);
n=strlen(S+1);
for(int i=1;i<=n;i++)
{
if(S[i]=='+'||S[i]=='-')
{
m++;
if(S[i-1]<'0'||S[i-1]>'9')
v[m]=1;
}
if(S[i]=='*'||S[i]=='/')
{
m++;
if(S[i-1]<'0'||S[i-1]>'9')
{
puts("0");
return 0;
}
}
}
if(S[n]<'0'||S[n]>'9')
{
puts("0");
return 0;
}
f[0][0]=1;
for(int i=0;i<=2*m;i++)
{
for(int j=0;j<=min(m,i);j++)
{
int k=i-j;
if(j<k)continue;
if(j<m)
f[i+1][j+1]=(f[i+1][j+1]+f[i][j])%mod;
if(j>0)
{
if(v[k+1])
f[i+1][j]=(f[i+1][j]+f[i-1][j-1])%mod;
else
f[i+1][j]=(f[i+1][j]+f[i][j])%mod;
}
}
}
printf("%d\n",f[2*m][m]);
}