题意
给定一个关于 xx 的表达式,形如下例:
按如下方法计算:
(((x×4)+2)3+8)×6(((x×4)+2)3+8)×6
运算符只有 ++ 加号、 乘号、 ^^ 幂运算三种,给定的式子中有 nn 次运算。
进行 次给定的操作:操作一表示对一个给定的 xx 值,求原式的结果并对 取模;操作二表示将 kk 位置上的运算符和运算符后面的数字改变。完成这些操作。
思路
本题数据范围较大,朴素的 O(nm)O(nm) 算法肯定会超时。
2939329393 看起来很奇怪,对它进行质因数分解,发现能拆成 7×13×17×197×13×17×19 。那只用算出对于小数字取模的结果,再用 CRTCRT 合并即可。
由于 xx 固定在算式的最前端,更改也只是对于一个确定的位置,我们可以尝试处理出区间进行合并,把算式中的部分划分成区间,对,就是线段树。
我们对于每一个质因子构造出一棵线段树,总共四棵线段树,每棵线段树中保存的信息是一个数组 ,其中 mpimpi 表示这个区间接收到最前端的数位 ii 时的运算结果,对于这棵树的模数 取模,则 mpmp 定义域和值域均为 [0,P)[0,P) 。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define N 50000
typedef long long LL;
using namespace std;
int _P[4]={7,13,17,19};
LL Pow(LL a,LL p,LL P)
{
p=(p-1)%(P-1)+1; //费马小定理优化
LL res=1;
while(p)
{
if(p&1)(res*=a)%=P;
(a*=a)%=P;
p>>=1;
}
return res;
}
struct CRT
{
LL A,P;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){x=1,y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
return;
}
LL Exgcd(LL A,LL B,LL C)
{
LL k1,k2;
exgcd(A,B,k1,k2);
return (k1*C%B+B)%B;
}
void kase_insert(CRT _)
{
LL res=Exgcd(P,_.P,_.A-A);
A+=res*P;
P=P/gcd(P,_.P)*_.P;
return;
}
};
struct node
{
int L,R,P;
int mp[20];
node operator +(const node &_)const
{
node res;
res.L=L,res.R=_.R,res.P=P;
FOR(i,0,P-1)res.mp[i]=_.mp[mp[i]];
return res;
}
void reset(char c,int d)
{
if(c=='+')
FOR(i,0,P-1)
mp[i]=(i+d)%P;
else if(c=='*')
FOR(i,0,P-1)
mp[i]=(i*d)%P;
else
FOR(i,0,P-1)
mp[i]=Pow(i,d,P);
return;
}
};
struct SegmentTree
{
node nd[N+3<<2];int P;
void build(int k,int L,int R,char *str,int *arr)
{
if(L==R)
{
nd[k].L=L,nd[k].R=R,nd[k].P=P;
nd[k].reset(str[L],arr[L]);
return;
}
build(k<<1,L,L+R>>1,str,arr);
build(k<<1|1,(L+R>>1)+1,R,str,arr);
nd[k]=nd[k<<1]+nd[k<<1|1];
return;
}
void update(int k,int loc,char c,int d)
{
if(nd[k].L==nd[k].R)
{
nd[k].reset(c,d);
return;
}
if(loc<=(nd[k].L+nd[k].R>>1))
update(k<<1,loc,c,d);
else update(k<<1|1,loc,c,d);
nd[k]=nd[k<<1]+nd[k<<1|1];
return;
}
}ST[4];
char str[N+3];int arr[N+3];
int main()
{
int T;
scanf("%d",&T);
FOR(Ti,1,T)
{
printf("Case #%d:\n",Ti);
int n,m;
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf(" %c%d",&str[i],&arr[i]);
FOR(i,0,3)
{
ST[i].P=_P[i];
ST[i].build(1,1,n,str,arr);
}
FOR(i,1,m)
{
int kase,a,b;char c;
scanf("%d",&kase);
if(kase==1)
{
scanf("%d",&a);
CRT sum;
FOR(i,0,3)
{
if(!i)sum=(CRT){ST[i].nd[1].mp[a%_P[i]],_P[i]};
else sum.kase_insert((CRT){ST[i].nd[1].mp[a%_P[i]],_P[i]});
}
printf("%lld\n",sum.A);
}
else
{
scanf("%d %c%d",&a,&c,&b);
FOR(i,0,3)ST[i].update(1,a,c,b);
}
}
}
return 0;
}