【CodeForces】115D Unambiguous Arithmetic Expression 组合数学

Description(translated)

定义明确表达式 UAE U A E
1. 自然数都是 UAE U A E (带前导零的也算)——换句话说,数字串是 UAE U A E
2. 对于两个 UAE U A E X X Y (X)+ororor/(Y) ( X ) + o r − o r ∗ o r / ( Y ) UAE U A E
3. 对于一个 UAE U A E X X +(X) (X) − ( X ) 都是 UAE U A E
给出一个 UAE U A E ,它的括号都被删掉了,请求出可以满足这个条件的 UAE U A E 的个数

Input

一行,一个字符串,仅包含 +/ + 、 − 、 ∗ 、 / 以及数字 09 0 到 9 ,不超过 2000 2000 个字符

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

好神的一道题……
第一,区间 DP D P O(n3) O ( n 3 ) 不说了,简单

然后,如果没有连续的符号,很显然每个符号都等价了
设符号有 a a 个,那么ans=f[a+1]=i=1af[i]f[a+1i] f[1]=1 f [ 1 ] = 1
可以 O(n) O ( n ) 求出,当然数据范围小, O(n2) O ( n 2 ) 也没人打你

现在我们考虑有连续的符号该怎么办
先简单判一下无解
1. 如果 / ∗ 、 / 前面是运算符或者前面没有字符,无解
2. 如果末尾出现运算符,无解
然后就都是有解的了……

考虑一下删去括号之前的 UAE U A E ,为其添加一些奇怪的东西——在正负号前面添加一组括号。举个栗子—— (+(233))(8) ( + ( 233 ) ) − ( 8 ) 变成 (()+(233))(8) ( ( ) + ( 233 ) ) − ( 8 )
由于每个运算符前后都有括号,我们去掉后方括号,上式变成 (()+233)8 ( ( ) + 233 ) − 8 ,省略数字和运算符就变成了 (()) ( ( ) )

神奇的是,每个运算符号恰好对应一个右括号,运算的顺序和括号的运算顺序也是一样的,不难发现,每个满足条件的 UAE U A E 恰好对应一个合法括号序列,每个合法括号序列也恰好对应一个满足条件的 UAE U A E 。但是考虑一下作为正负号的 + + 、 − ,可以发现它的右括号前面是空的,也就是说必须左右括号紧挨着才可以,写一个组合数学 DP D P 不是什么难事。

做这道题的时候莫名想起了 prufer p r u f e r 数列,每一棵有标号无根树都可以转化为唯一的 prufer p r u f e r 数列,而每一个 prufer p r u f e r 数列都可以转化为唯一的有标号无根树,而树不容易计数,但 prufer p r u f e r 数列却很好计数,这样一转化就简单多了。

这题也是一样,把不好计数的 UAE U A E 转化为比较容易计数的括号序列,好神啊……

PS: P S : 我还是太弱了,看了题解还是WA了,忘了特判末尾有运算符的无解情况……

Code: C o d e :

#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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值