HDU 6145 Arithmetic of Bomb II(数学+矩阵快速幂)

256 篇文章 0 订阅
52 篇文章 0 订阅

Description

众所周知,度度熊非常喜欢数字。

它最近在学习小学算术,沉迷于计算A+B中不能自拔。

但是这回的算术题可并不简单,由于含有表示bomb的#号,度度熊称之为 Arithmetic of Bomb。

img

Arithmetic of Bomb的目的与普通算术一样,就是计算一些Bomb Expression的结果。比如,”1-2+3”的结果为2。然而,bomb,也就是#号,会展开一些普通表达式,这会导致需要计算的式子超出了度度熊所能理解的范畴。比如”(1-2+3)#(3)”表示”1-2+3”出现了3次,将会被展开为”1-2+31-2+31-2+3”。

为了方便理解,下面给出了Bomb Expression的BNF表示。

<bomb expression> := <bomb term> | <bomb expression> <bomb term>
<bomb term> := <bomb statement> | '(' <bomb statement> ')' '#' '(' <number> ')'
<bomb statement> := <bomb element> | <bomb statement> <bomb element>
<bomb element> := <digit> | '+' | '-' | '*'
<normal expression> := <norm term> | <normal expression> '+' <norm term> | <normal expression> '-' <norm term>
<norm term> := <number> | <norm term> '*' <number>
<number> := <digit> | <non-zero-digit> <number>
<digit> := '0' | <non-zero-digit>
<non-zero-digit> := '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'

请先将Bomb Expression中所有的#号展开,使其成为Normal Expression(题目的输入保证展开后是一个合法的Normal Expression),再来计算这个表达式的结果。

Input

第一行为T,表示输入数据组数。

每组数据包含一个Bomb Expression。

●1≤T≤50

●1≤length(Bomb Statement)≤10

●1≤length(Number in Bomb term)≤10

●1≤length(Bomb Expression)≤300 000

Output

对每组数据输出表达式的结果,结果对 1 000 000 007 取模。

Sample Input

6
1-2+3
(1-2+3)#(3)
(1)#(3)
(1+)#(2)1
(2*3+1)#(2)
(2)#(2)1+1(2)#(2)

Sample Output

2
60
111
3
43
343

Solution

将表达式展开不现实,考虑用矩阵简化运算,由于加法和减法类似,所以下面只讨论数字、加法和乘法

Mia,b,c,d,e=ab+c+de=110ibabc000de000d0001

1.在 Mia,b,c,d,e 两端加位

若要通过左乘一个矩阵实现在 ab+c+de 左端的 a 加上一个最高位f,即要把 Mia,b,c,d,e 变成 Mi+1fa,b,c,d,e ,只需把矩阵中的 (10ib,ab)T 变成 (10i+1b,fab)T ,由于 fab=ab+f10ib ,故有

(10i+1bfab)=(10f01)(10ibab)

若要通过右乘一个矩阵实现在 ab+c+de 右端的 e 加上一个最低位f,即要把 Mia,b,c,d,e 变成 Mia,b,c,d,ef ,只需把矩阵中的 (de,d) 变成 (def,d) ,由于 def=10de+df ,故有
(defd)=(ded)(10f01)

故对于每个一位数 f ,令
Mf=1000010f000100001

那么通过对 Mia,b,c,d,e 左乘右乘 Mf 即可实现在左边右边加一位 f

2.在Mia,b,c,d,e两端加数字

若要通过左乘一个矩阵实现在 ab+c+de 左端加一个数字 f ,即要把Mia,b,c,d,e变成 M1f,1,ab+c,d,e ,只需把矩阵中的 (1,10ib,ab,c)T 变成 (1,101,f1,ab+c)T ,故有

1101f1ab+c=110f0000000010001110ibabc =Mf1100000000010001110ibabc

若要通过右乘一个矩阵实现在 ab+c+de 右端加一个数字 f ,即要把Mia,b,c,d,e变成 Mia,b,c+de,1,f ,只需把矩阵中的 (c,de,d,1) 变成 (c+de,f,1,1) ,故有
(c+def11)=(cded1)1100000f00010001=(cded1)1100000000010001Mf

故对于加号,令
M+=1100000000010001

那么通过对 Mia,b,c,d,e 左乘右乘 M+ 即可实现在左边右边带上加号

注:类似的可以定义减号

M=1100000000010001

3.在 Mia,b,c,d,e 两端乘数字

若要通过左乘一个矩阵实现在 ab+c+de 左端乘一个数字 f ,即要把Mia,b,c,d,e变成 M1f,ab,c,d,e ,只需把矩阵中的 (10ib,ab)T 变成 (10ab,fab)T ,故有

(10abfab)=(0010f)(10ibab)=(10f01)(0010)(10ibab)

若要通过右乘一个矩阵实现在 ab+c+de 右端乘一个数字 f ,即要把Mia,b,c,d,e变成 Mia,b,c,de,f ,只需把矩阵中的 (de,d) 变成 (def,de) ,故有
(defde)=(ded)(f010)=(ded)(0010)(10f01)

故对于乘号,令
M=1000000001000001

那么通过对 Mia,b,c,d,e 左乘右乘 M 即可实现在左边右边带上乘号

综上,通过把每一个位 f,+,, 分别变成 Mf,M+,M,M 后,原表达式即转化为一些矩阵的乘积,而对于重复的表达式,只需求矩阵幂即可

注:

1. M 的意义:当在 ab+c+de 右方加上 f 时,我们先把 f 用和加法相同的方法放在了第四行第二列,然后用一个符号变量记录这个负号,当后面又出现加法或者减法时,这个f会被并到第四行第四列中的 c+de 中,此时由于通过符号变量的记录我们用 c+de 减去 f 而不是加上f来得到 c+def 的正确值

2.当处理重复表达式时,我们会用一个矩阵来表示这个重复表达式,然后用矩阵幂去快速计算,但是由于在计算过程中需要用一个符号变量记录 Mia,b,c,d,e 中的 de 的真实符号,故对于第一个该表达式,其符号取决于之前表达式继承下来的符号,而后面的表达式的符号是取决于这个表达式中的最后一个运算是加还是减,故第一个表达式要单独计算,后面的重复部分再用幂运算加速

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define maxn 300005
#define mod 1000000007
vector<string>term;
vector<ll>num;
char c[maxn]; 
int T;
void Deal()
{
    term.clear(),num.clear();
    int n=strlen(c);
    for(int i=0;i<n;)
    {
        string s;
        if(c[i]=='(')
        {
            i++;
            while(c[i]!=')')s.push_back(c[i++]);
            i+=3;
            ll t=0;
            while(c[i]!=')')t=t*10+c[i++]-'0';
            if(t)term.push_back(s),num.push_back(t);
            i++;
        }
        else
        {
            while(i<n&&c[i]!='(')s.push_back(c[i++]);
            term.push_back(s),num.push_back(1);
        }
    }
}
typedef ll Mat[4][4];
Mat A,B,C;
void Mul(Mat &a,Mat b)
{
    Mat c;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
        {
            c[i][j]=0;
            for(int k=0;k<4;k++)
                c[i][j]+=a[i][k]*b[k][j];
        }
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            a[i][j]=(c[i][j]%mod+mod)%mod;
}
void Pow(Mat &a,ll b)
{
    if(b==1)return ;
    Mat c;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            c[i][j]=(i==j);
    while(b)
    {
        if(b&1)Mul(c,a);
        Mul(a,a);
        b>>=1;
    }
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            a[i][j]=c[i][j];
}
ll Count()
{
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            A[i][j]=(i==j);
    int sign=1;
    for(int k=0;k<term.size();k++)
    {
        string s=term[k];
        ll t=num[k];
        int n=s.size();
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                B[i][j]=(i==j);
        for(int i=0;i<n;i++)
        {
            memset(C,0,sizeof(C));
            C[0][0]=C[3][3]=1;
            if(s[i]>='0'&&s[i]<='9')
                C[1][1]=10,C[2][1]=s[i]-'0',C[2][2]=1;
            else if(s[i]=='+')
                C[1][0]=sign,C[3][2]=1,sign=1;
            else if(s[i]=='-')
                C[1][0]=sign,C[3][2]=1,sign=-1;
            else C[1][2]=1;
            Mul(B,C); 
        }
        Mul(A,B);
        if(t>1)
        {
            for(int i=0;i<4;i++)
                for(int j=0;j<4;j++)
                    B[i][j]=(i==j);
            for(int i=0;i<n;i++)
            {
                memset(C,0,sizeof(C));
                C[0][0]=C[3][3]=1;
                if(s[i]>='0'&&s[i]<='9')
                    C[1][1]=10,C[2][1]=s[i]-'0',C[2][2]=1;
                else if(s[i]=='+')
                    C[1][0]=sign,C[3][2]=1,sign=1;
                else if(s[i]=='-')
                    C[1][0]=sign,C[3][2]=1,sign=-1;
                else C[1][2]=1;
                Mul(B,C); 
            }
            Pow(B,t-1);
            Mul(A,B);
        }
    }
    ll ans=(A[2][0]+A[3][0])%mod;
    if(sign==1)ans=(ans+A[2][1]+A[3][1])%mod;
    else ans=(ans-A[2][1]-A[3][1])%mod;
    ans=(ans+mod)%mod;
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",c);
        Deal();
        printf("%I64d\n",Count()); 
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值