[字符串] 一元一次方程

文章目录


题目

题目描述

S L O N SLON SLON是一个调皮的学生,为了让他静下心来,老师给他出了一道数学题:

给定表达式 A A A A A A中含有变量 x x x + , − , ∗ , ( , ) +,-,*,(,) +,,,(,)这些符号,括号成对出现,一个算术运算符均对应两个操作数,不能出现 ( − 5 ) (-5) (5)或者 ( 4 + − 5 ) (4+-5) (4+5)等,乘号不能省略,并且表达式 A A A x x 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) or x+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)) or x(x+x(1+x))

A A%M==P A时,最小的 x x x

输入

第一行输入一个表达式 A A A ( 1 ≤ ∣ A ∣ ≤ 100000 ) (1 ≤|A|≤ 100000) (1A100000)
第二行输入两个整数 P P P ( 0 ≤ P ≤ M − 1 ) (0 ≤ P ≤ M −1) (0PM1) M M M ( 1 ≤ M ≤ 1000000 ) (1 ≤ M ≤ 1000000) (1M1000000)

输出

输出最小的非负 x x x

样例输入

5+3+x
9 10

样例输出

1

题解

这个题其实考试时想到了,但细节好像有点多。。。

这个题思路很简单,把字符串处理出来,暴力答案(考试时还以为是拓展欧几里得,结果忘了怎么求二元一次不定方程的通解,然后果断暴力居然撞上了正解 好水呀

主要就是这个字符串处理比较细节,这个中缀表达式有点毒瘤。可以考虑把这个中缀转换成后缀,然后就可以处理了。我是直接搞的中缀

定义两个栈,一个存数字,一个存符号
数字没有什么好处理的,我就直接写符号的处理:

1、 ( ( ( 直接入栈
2、 + , − , ∗ +,-,* +,, 入栈时判断如果当前的栈顶为 ∗ * ,就将这个乘法运算处理掉
3、 ) ) ) 弹出栈中元素,直到弹到左括号,将弹出的符号预算全部处理

最后可以枚举答案,但拓展欧几里得明显会更快,在此讲一下:
题目给出的中缀表达式最终一定可以表示为 a x + b ax+b ax+b的形式
∵ ( a x + b ) % m = p ∴ a x + b = k m + p ⇒ a x − k m = p − b \because (ax+b)\% m=p\\ \therefore ax+b=km+p\\ \Rightarrow ax-km=p-b (ax+b)%m=pax+b=km+paxkm=pb
至于求 a , b a,b a,b,我们只需要将 x = 0 和 x = 1 x=0和x=1 x=0x=1代入原式,分别可得 b 和 a + b b和a+b ba+b,然后就可以求出 a a a
最后求符合条件的最优解即可

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <stack>

using namespace std;

template <typename T>
T Fabs(T x) {return x < 0 ? -x : x;}

#define LL long long

const int N = 100005;

stack <int> sta1;
stack <char> sta2;

LL P, M, a1, a2, ansx, ansy;
int len;
char a[N];

int solve(int x) {
	while(!sta1.empty()) sta1.pop();
	while(!sta2.empty()) sta2.pop();
	for(int i = 0; i <= len + 1; i ++) {
		if(a[i] != ')') {
			if(a[i] >= '0' && a[i] <= '9') {
				LL num = a[i] - '0';
				while(a[i + 1] >= '0' && a[i + 1] <= '9') {
					num = (num * 10 + a[i + 1] - '0') % M;
					i ++;
				}
				sta1.push(num);
			}
			if(a[i] == 'x')
				sta1.push(x);
			if(a[i] == '+' || a[i] == '-' || a[i] == '*') {
				if(sta2.top() == '*') {
					sta2.pop();
					LL num1 = sta1.top();
					sta1.pop();
					LL num2 = sta1.top();
					sta1.pop();
					sta1.push(num1 * num2 % M);
				}
				sta2.push(a[i]);
			}
			if(a[i] == '(')
				sta2.push('(');
		}
		else {
			if(sta2.top() == '*') {
				sta2.pop();
				LL num1 = sta1.top();
				sta1.pop();
				LL num2 = sta1.top();
				sta1.pop();
				sta1.push(num1 * num2 % M);
			}
			while(!sta2.empty()) {
				if(sta2.top() == '(') {
					sta2.pop();
					break;
				}
				else {
					LL num1 = sta1.top();
					sta1.pop();
					LL num2 = sta1.top();
					sta1.pop();
					if(sta2.top() == '+')
						sta1.push((num1 + num2) % M);
					else if(sta2.top() == '-')
						sta1.push((num2 - num1 + M) % M);
					else
						sta1.push(num1 * num2 % M);
					sta2.pop();
				}
			}
		}
	}
	return sta1.top();
}

LL exgcd(LL a, LL b, LL &x, LL &y) {
	if(b == 0) {
		x = 1, y = 0;
		return a;
	}
	else {
		LL ret = exgcd(b, a % b, y, x);
		y -= x * (a / b);
		return ret;
	}
}

int main() {
	scanf("%s", a + 1);
	scanf("%lld%lld", &P, &M);
	len = strlen(a + 1);
	a[0] = '(', a[len + 1] = ')';
	LL sol1 = solve(0);
	LL sol2 = solve(1);
	a1 = (sol2- sol1 + M) % M, a2 = sol1;
	LL GCD = exgcd(a1, M, ansx, ansy);
	ansx *= ((P - a2) / GCD);
	while(ansx >= 0)
		ansx -= M / GCD;
	while(ansx < 0)
		ansx += M / GCD;
	printf("%lld\n", ansx);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值