题意
给定一个能够化简为函数 f(x)=kx+bf(x)=kx+bf(x)=kx+b 的中缀表达式,求最小的 xxx 使得 f(x)f(x)f(x) 模 mmm 为 PPP。
思路
之前喜欢用后缀表达式计算,模拟考时被卡了才知道难受。
表达式求值
本蒟蒻尝试了一下硬算中缀表达式,思路如下:
- 如果形如
(...),递归...(去除括号),但是不要和(...)(...)(...)混了,需要判定一下。 - 在序列中查找优先级最低的运算符(如有多个取第一个)。
- 若没有一个运算符和括号,则要么为
x,要么为数字判定即可。
求 f(x)f(x)f(x) 的方法:
因为直接代入特值可以用上述方法计算,我们考虑:
存在两个式子 f(0)=bf(0)=bf(0)=b 和 f(1)=k+bf(1)=k+bf(1)=k+b,且 f(0)f(0)f(0) 和 f(1)f(1)f(1) 可求得。
解得 b=f(0)b=f(0)b=f(0) 和 k=f(1)−f(0)k=f(1)-f(0)k=f(1)−f(0)
最后讲一下 f(x)f(x)f(x) 已知时如何求出 ansansans:
- 暴力枚举区间 [0,m)[0,m)[0,m)
- exgcd 计算
综上,本题得解。
Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5;
int p,m;string s;
int u[MAXN+1],cnt=0,lk[MAXN+1];
int Calc(int l,int r,int xnum){
// cout<<l<<' '<<r<<"TTTT\n";
int i=upper_bound(u+1,u+1+cnt,l)-u;
if(s[l]=='('){
bool fg=true;
while(u[i]<=r){
if(lk[u[i]]>lk[l]){
i++;
continue;
}
if(s[u[i]]=='+'||s[u[i]]=='-'||s[u[i]]=='*'){
fg=false;break;
}
i++;
}
if(fg)return Calc(l+1,r-1,xnum);
}
int p=-1;
while(u[i]<=r){
if(lk[u[i]]>lk[l]){
i++;
continue;
}
if(s[u[i]]=='+'){
return (Calc(l,u[i]-1,xnum)+Calc(u[i]+1,r,xnum)+m)%m;
}
if(s[u[i]]=='-'){
return ((Calc(l,u[i]-1,xnum)-Calc(u[i]+1,r,xnum))%m+m)%m;
}
if(s[u[i]]=='*')p=u[i];
i++;
}
if(p==-1){
if(l==r&&s[l]=='x')return xnum;
int num=0;
for(int i=l;i<=r;i++)num=(num*10+s[i]-'0')%m;
return num;
}
return (1ll*Calc(l,p-1,xnum)*Calc(p+1,r,xnum))%m;
}
void exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return;
}
int main(){
cin>>s;
cin>>p>>m;
int tmp=0;
for(int i=0;i<(int)s.length();i++){
if(s[i]=='+'||s[i]=='-'||s[i]=='*')u[++cnt]=i;
lk[i]=tmp;
if(s[i]=='('){
lk[i]=tmp;
tmp++;
}
else if(s[i]==')'){
tmp--;
lk[i]=tmp;
}
else lk[i]=tmp;
}
int b=Calc(0,(int)s.length()-1,0);
int bplusk=Calc(0,(int)s.length()-1,1);
int k=bplusk-b;
// int k,b;cin>>k>>b;B
// cout<<"f(x)="<<k<<"x+"<<b<<"$$$\n";
int t=__gcd(k,-m);
int xx,yy;
exgcd(k,-m,xx,yy);
long long x0=1ll*xx*(p-b)/t,y0=1ll*yy*(p-b)/t;
// cout<<x0<<' '<<y0<<"vvvv\n";
while(x0<0){
if(t<0){
x0-=m/t;
y0-=k/t;
}
else{
x0+=m/t;
y0+=k/t;
}
}
while(x0>0){
if(t>0){
if(x0-m/t<0)break;
x0-=m/t;
y0-=k/t;
}
else{
if(x0+m/t<0)break;
x0+=m/t;
y0+=k/t;
}
}
printf("%lld\n",x0);
// cout<<b<<"***\n";
return 0;
}

被折叠的 条评论
为什么被折叠?



