slon题解

2019暑期联考第三场T3——slon

slon

题目描述

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 ) x + 3 ∗ x + 4 ∗ ( 5 + 3 ∗ ( 2 + x − 2 ∗ x ) ) A=\left\{\begin{array}{c}5 + x∗(3 + 2)\\x + 3∗x + 4∗(5 + 3∗(2 + x−2∗x))\end{array}\right. A={5+x(3+2)x+3x+4(5+3(2+x2x))
不合理表达式
A = { 5 ∗ ( 3 + x ∗ ( 3 + x ) ) x ∗ ( x + x ∗ ( 1 + x ) ) . A=\left\{\begin{array}{c}5∗(3 + x∗(3 + x))\\x∗(x + x∗(1 + x))\end{array}\right.. A={5(3+x(3+x))x(x+x(1+x)).
A ( m o d ) M = = P A(mod)M==P A(mod)M==P时,最小的 x x x.

输入

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

样例一
5+3+x
9 10

样例二
20+3+x
0 5


样例三
3*(x+(x+4)*5)
1 7

输出

输出最小的非负 x x x

样例一
1

样例二
2

样例三
3

题解

一道十分经典的字符串处理四则运算题。
可以作为处理四则运算的典例。
首先,我们应该先搞清楚这个题的难点到底是什么。
其实看过题目都应该知道,处理字符串应该是这个题的核心难点
至于什么取模什么相等的都去死吧,我要用爆搜之剑审判你
那么,这个有四则运算在其中的字符串应该如何处理呢?
那么,这里就有了许多种处理字符串的方式:

  • 保持原有中则表达式,利用栈处理字符串
  • 将中则表达式转为后缀表达式,利用栈与递归处理
  • (实在不知道这种思路叫什么) 将每一个数字与一个运算符绑在一起,利用递归处理字符串。

这篇文章将侧重讲解第一种方法 (不然为什么把它放在前面)
首先,我们来看一下这道题处理麻烦的原因:

  • 括号改变计算顺序
  • 字符 x x x既不是运算符
  • 括号前若有 ′ − ′ '-' ,那么括号里面的所有符号(除乘号)都要进行反转操作
  • ′ ∗ ′ '*' ′ + ′ ( ′ − ′ ) '+'('-') +()一起时,应先计算乘号的内容

也就是说,如果我们把以上问题处理好,那么此题就迎刃而解了。
要解决以上问题,这里要用一种封装思想:
这道题题目似乎很难,但是可以浓缩为几句话
将一个四则表达式化简为 K x + B Kx+B Kx+B,且使得 ( K x + B ) m o d M = = P (Kx+B)modM==P (Kx+B)modM==P这就是这道题的精髓。
也就是说,我们要得到的是 K K K B B B,而所谓封装,就是将每一个括号(也不一定是括号)的 K K K R R R进一个 s t r u c t struct struct或者是 p a i r pair pair中,再用这个 s t r u c t struct struct或者 p a i r pair pair与其他的直接进行加、减、乘即可。(这句话十分重要,但若是不懂,看看代码应该也能理解)
剩下的,和一般的四则运算处理就差不多了。
那么我们定义: p a i r pair pair中的 f i r s t first first为当前算式中 x x x的系数, s e c o n d second second为常数的值。
通过 p a i r pair pair的两个数的定义,很容易就可以得出两个 p a i r pair pair相加、减、乘时的具体操作。
为了更方便理解,这里我用数学符号进行推到(其实十分简单)
通过 p a i r pair pair定义,令 p a i r 1 = ( K 1 , B 1 ) pair_1=(K_1,B_1) pair1=(K1,B1) p a i r 2 = ( K 2 , B 2 ) pair_2=(K_2,B_2) pair2=(K2,B2).
那么 p a i r 1 ± p a i r 2 = ( K 1 x + B 1 ) ± ( K 2 x + B 2 ) pair_1±pair_2=(K_1x+B_1)±(K_2x+B_2) pair1±pair2=(K1x+B1)±(K2x+B2) = > p a i r r e t u r n = ( K 1 ± K 2 , B 1 ± B 2 ) =>pair_{return}=(K_1±K_2,B_1±B_2) =>pairreturn=(K1±K2,B1±B2)紧接着 p a i r 1 ∗ p a i r 2 = ( K 1 x + B 1 ) ( K 2 x + B 2 ) pair_1*pair_2=(K_1x+B_1)(K_2x+B_2) pair1pair2=(K1x+B1)(K2x+B2) = > 原 式 = K 1 K 2 x 2 + ( K 1 + K 2 ) x + B 1 B 2 =>原式=K_1K_2x^2+(K_1+K_2)x+B_1B_2 =>=K1K2x2+(K1+K2)x+B1B2注意,因为题目保证这是一个一元一次的方程,那么 K 1 K_1 K1 K 2 K_2 K2中必有一个为 0 0 0,那么就不用考虑 x 2 x^2 x2了,那么 p a i r r e t u r n = ( K 1 B 2 + K 2 B 1 , B 1 B 2 ) pair_{return}=(K_1B_2+K_2B_1,B_1B_2) pairreturn=(K1B2+K2B1,B1B2)其他的操作就比较简单了。
下见代码。

#include<cstdio>
#include<stack>
#include<cstring>
#include<utility>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pll;
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
inline LL read(){
	#define cg (c=getchar())
	LL x,f=1;char c;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	return x*f;
	#undef cg
}
const LL MAXL=100000;
char str[MAXL|1];
LL P,M,len;
pll operator+(const pll& a,const pll& b){
//处理两个pair相加
	return mp((a.ft+b.ft)%M,(a.sd+b.sd)%M);
}
pll operator-(const pll& a,const pll& b){
	return mp((a.ft-b.ft+M)%M,(a.sd-b.sd+M)%M);
}
pll operator*(const pll& a,const pll& b){
	return mp((a.ft*b.sd+a.sd*b.ft)%M,a.sd*b.sd%M);
}
bool isnum(char c){
	return '0'<=c&&c<='9';
}
stack<char>ch;
stack<pll>sta;
bool cmpPrio(char a,char b){
	if(a=='('||b=='(')return 0;
	if(a=='*'||b=='+'||b=='-'||b==')')return 1;
	return 0;
}
pll solve(pll a,pll b,char ord){
	if(ord=='+')return a+b;
	if(ord=='-')return a-b;
	if(ord=='*')return a*b;
	return mp(0,0);
}
int main(){
	scanf("%s",str+1);
	len=strlen(str+1);
	P=read(),M=read();
	str[++len]=')';
	ch.push('(');
	for(LL i=1;i<=len;++i){
		// printf("i==%d\n",i);
		if(str[i]=='x')sta.push(mp(1,0));
		else if(isnum(str[i])){
			LL num=str[i]^48;
			while(isnum(str[i+1]))
				num=((num<<1)+(num<<3)+(str[++i]^48))%M;
			sta.push(mp(0,num));
		}
		else{
			while(cmpPrio(ch.top(),str[i])){
				pll x,y;
				x=sta.top();
				sta.pop();
				y=sta.top();
				sta.pop();
				sta.push(solve(y,x,ch.top()));
				ch.pop();
			}
			if(str[i]==')')ch.pop();
			else ch.push(str[i]);
		}
	}
	pll res=sta.top();
	for(LL i=0;i<=M;++i)if((res.ft*i+res.sd)%M==P)
		return !printf("%lld\n",i);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值