2019暑假七考~~一元一次方程[slon]~~(中缀转后缀求值,你从未见过的全新操作)

14 篇文章 1 订阅
7 篇文章 0 订阅


题目(1000ms)

描述
SLON是一个调皮的学生,为了让他静下心来,老师给他出了一道数学题:
给定表达式A,A中含有变量x和+,-,*,(,)这些符号,括号成对出现,一个算术运算符均对应两个操作数,不能出现(-5)或者(4±5)等,乘号不能省略,并且表达式A中x只能是一阶,即一阶表达式:
合理表达式 A = 5 + x ∗ ( 3 + 2 ) o r x + 3 ∗ x + 4 ∗ ( 5 + 3 ∗ ( 2 + x − 2 ∗ x ) ) . A=5 + x∗(3 + 2) or x + 3∗x + 4∗(5 + 3∗(2 + x−2∗x)). A=5+x(3+2)orx+3x+4(5+3(2+x2x)).

不合理表达式 A = 5 ∗ ( 3 + x ∗ ( 3 + x ) ) o r x ∗ ( x + x ∗ ( 1 + x ) ) . A=5∗(3 + x∗(3 + x)) or x∗(x + x∗(1 + x)). A=5(3+x(3+x))orx(x+x(1+x)).

A ≡ P ( m o d M ) A \equiv P\pmod M AP(modM)时,最小的x
输入
The first line of input contains the expression A .

The second line of input contains two integers P , M .

The arithmetic expression A will only consists of characters +, -, *, (, ), x and digits from 0 to 9.

The brackets will always be paired, the operators +, - and * will always be applied to exactly two values (there will not be an expression (-5) or (4±5)) and all multiplications will be explicit (there will not be an expression 4(5) or 2(x)).(上面那些情况不合理,数据中不会出现)
输出
输出最小的非负x
范围
1 ≤ ∣ A ∣ ≤ 100000 , 0 ≤ P ≤ M − 1 , 1 ≤ M ≤ 1000000 1 ≤|A|≤ 100000,0 ≤ P ≤ M −1,1 ≤ M ≤ 1000000 1A100000,0PM1,1M1000000
注意, ∣ A ∣ |A| A是指字符串 A A A的长度
样例

样例输入1
5+3+x
9 10
样例输出1
1

样例输入2
20+3+x
0 5
样例输出2
2

样例输入3
3*(x+(x+4)*5)
1 7
样例输出3
1

思路

大体思路
应为最后的表达式一定是 f ( x ) = k x + b f(x)=kx+b f(x)=kx+b,所以我们可以分别求出
f ( 0 ) f(0) f(0) f ( 1 ) f(1) f(1) f ( 0 ) = b , f ( 1 ) = k + b f(0)=b, f(1)=k+b f(0)=b,f(1)=k+b,这样我们就可以求出 k k k b b b,最后暴力枚举或者求逆元随便

实现时的思路
由于题目是中缀表达式,计算起来比较复杂,所以我们需要把中缀表达式转为后缀表达式
当然,如果你觉得你能够计算中缀表达式,Orz,但是,这道题神仙数据,像()+x,(x)+(344)+(13566),(((((((x)))))))等等,加油,我相信你
大佬可以略过下面两篇博客
中缀转后缀
中缀转后缀并求值

最后求答案时,直接暴力枚举或者求逆元(忘得差不多了,好多方法)

由于我觉得中缀转后缀再求值太麻烦,所以我在转后缀的同时就计算了。
题目不就是求一个系数和常数么?那就存两个栈,求常数的那个栈就把x存为0,其他数原封不动存入栈中;求系数的那个栈就把x存为1,其他数存为0,加减法时就用本栈里的数,在做乘法的时候,若其中只有一个数为0,那么就乘上另一个栈里对应的数(两栈同时计算),若两数都为零,那没办法,push(0)。(搞了很久啊)

最后两栈中一个就是常数 ( b ) (b) (b),一个就是系数 ( k ) (k) (k),再用之前的办法求出 a n s ans ans就行了
注意,最后的数可能很大,操作的时候记得 % m \% m %m


Code
#include <map>
#include <stack>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long

map<char, bool>Level;
stack<char>S;//字符
stack<LL>num[2];//num[0]存常数,num[1]存系数

string s;

int l;

LL _x, _sum, p, m, tot;

inline void calc(char cc){
    LL a = 0, b = 0, a1 = 0, b1 = 0;
    a = num[0].top();
    num[0].pop();
    b = num[0].top();
    num[0].pop();
    a1 = num[1].top();
    num[1].pop();
    b1 = num[1].top();
    num[1].pop();/*
    printf("%c\n", cc);
    printf("%d %d\n", a1, b1);*/
    if( cc == '*' ){//乘法有特例
        num[0].push(a*b%m);
        if( a1 == 0 && b1 != 0 )
            num[1].push(a*b1%m);
        if( a1 != 0 && b1 == 0 )
            num[1].push(a1*b%m);
        if( a1 != 0 && b1 != 0 )
            num[1].push(a1*b1%m);
        if( a1 == 0 && b1 == 0 )
            num[1].push(0);
    }
    if( cc == '-' ){
        num[1].push((b1-a1+m)%m);
        num[0].push((b-a+m)%m);
    }
    if( cc == '+' ){
        num[1].push(a1+b1%m);
        num[0].push(a+b%m);
    }
}

int main(){
    cin >> s;
    s += ")";//手动加括号
    scanf("%lld%lld", &p, &m);
    l = s.length();
    Level['-'] = Level['+'] = 0;
    Level['*'] = 1;
    Level['('] = Level[')'] = 2;
    S.push('(');
    for(int i = 0; i < l; i ++){
        if( isdigit(s[i]) && !(i==0?0:isdigit(s[i-1])) ){//特殊情况
            num[0].push(s[i]^48);
            num[1].push(0);
        }
        else if( isdigit(s[i]) && (i==0?0:isdigit(s[i-1])) ){
            LL sc = num[0].top();
            num[0].pop();
            num[0].push(sc*10%m+(s[i]^48)%m);
        }
        if( s[i] == 'x' ){
            num[0].push(0);
            num[1].push(1);
            continue;
        }
        if( s[i] == '-' || s[i] == '+' || s[i] == '*' ){
            while( !S.empty() && Level[s[i]] <= Level[S.top()] && S.top() != '(' ){
                //printf("\n1 %c\n", s[i]);老祖调试法
                calc(S.top());
                S.pop();
            }
            S.push(s[i]);
        }
        if( s[i] == ')' ){
            while( !S.empty() && S.top() != '(' ){
                //printf("\n2\n");
                if( S.top() == '-' || S.top() == '+' || S.top() == '*' )
                    calc(S.top());
                S.pop();
            }
            S.pop();
        }
        if( s[i] == '(' )
            S.push(s[i]);
    }
    _sum = num[0].top()%m, _x = num[1].top()%m;
    while( tot < m ){//暴力
        if( (tot*_x+_sum)%m == p ){
            printf("%lld\n", tot);
            break;
        }
        tot++;
    }
    return 0;
}
//(10)+(((3-4+5*7+x))+5*9-(4)*9-x*1+(x))
/*
((((((x+1)+1)+1)+1)+1)+1)
*/


代码实现Tips
  • 中缀转后缀求值全新模板,你值得拥有
  • 因为最后要把字符栈中所有字符全弹出,所以你阔以手动再原字符串两边加括号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值