热身训练1 Calculator
题目出处:Calculator
简要题意:
你有一个确定的函数,f(x)=+...*...^...,其中共有n个操作,从左到右依次计算。
共有m次询问,我们每次询问,1.会修改f(x)中的操作;2.输出f(x)%29393
分析:
分解29393可以得到7*13*17*19,这几个数都很小,很容易预处理!
于是我们可以将一个f(x)%29393分成四个小方程
我们令 a1=f(x)%7; a2=f(x)%13; a3=f(x)%17; a4=f(x)%19。
那么由中国剩余定理,可以得到模29393意义下的唯一解
虽说CRT求得的,是所有正整数解的最小解,
但是,由上图我们可以知道,如果我们最终的答案要模上29393,那么CRT求得的答案就是唯一解。
接下来,我们只需要能够快速求得这四个数,即可用CRT求得答案。
由于这4个模数都很小,我们可以提前预处理出 当x小于模数时的f(x)的值
我们可以用线段树来实现函数功能!
tr[p].val[i][j] = tr[rs].val[i][tr[ls].val[i][j]];
其中val[i][j]表示:处理第i个模数,输入的x值为j
由于我们的计算是从左到右依次计算,所以父亲节点的val值,为当“输入值为左儿子x的返回值时”的右儿子的返回值。
当我们要修改函数时,只需要从叶子节点开始修改,一步一步推到根节点即可。
最后我们的a1,a2,a3,a4分别为根节点处的val[1][x],val[2][x],val[3][x],val[4][x]
#include<bits/stdc++.h> using namespace std; #define re register int #define LL long long #define int long long const int N=5e4+5; inline int ksm(int x, int y, const int pp) { x %= pp; y %= (pp-1); int ret = 1; while(y) { if(y&1) ret = ret*x%pp; x=x*x%pp; y>>=1; } return ret; } const LL chu[5]={7, 13, 17, 19}; const LL MOD = 29393; struct segment{int a, b, val[5][20];}tr[N<<2]; void build(const int p, const int l, const int r) { tr[p].a=l; tr[p].b=r; if(l != r) { int mid=(l+r)>>1, ls=p<<1, rs=p<<1|1; build(ls, l, mid); build(rs, mid+1, r); for(re i=0;i<4;++i) for(re j=0;j<chu[i];++j) tr[p].val[i][j] = tr[rs].val[i][tr[ls].val[i][j]]; } else { int ty, x;char ch; scanf("%c%lld",&ch,&x); if(ch=='+')ty=0;else if(ch=='*')ty=1;else ty=2; getchar(); for(re i=0;i<4;++i) { for(re j=0;j<chu[i];++j) { if(ty == 0) tr[p].val[i][j] = (j+x)%chu[i]; if(ty == 1) tr[p].val[i][j] = j*x%chu[i]; if(ty == 2) tr[p].val[i][j] = ksm(j, x, chu[i]); } } } } void modi(const int p, const int pos, const int x, const int ty) { if(tr[p].a == pos && pos == tr[p].b) { for(re i=0;i<4;++i) { for(re j=0;j<chu[i];++j) { if(ty == 0) tr[p].val[i][j] = ((j+x)%chu[i]+chu[i])%chu[i]; if(ty == 1) tr[p].val[i][j] = j*x%chu[i]; if(ty == 2) tr[p].val[i][j] = ksm(j, x, chu[i]); } } return; } int mid=(tr[p].a+tr[p].b)>>1, ls=p<<1, rs=p<<1|1; if(pos <= mid) modi(ls, pos, x, ty); else modi(rs, pos, x, ty); for(re i=0;i<4;++i) for(re j=0;j<chu[i];++j) tr[p].val[i][j] = tr[rs].val[i][tr[ls].val[i][j]]; } inline LL getans(const int pp) { LL ret=0; for(re i=0;i<4;++i) { int Mi=MOD/chu[i]; int t=ksm(Mi,chu[i]-2,chu[i]); ret=(ret+tr[1].val[i][pp%chu[i]]*Mi*(t%chu[i]))%MOD; } return (ret+MOD) % MOD; } int n, m; inline void work() { scanf("%lld%lld",&n,&m); getchar(); build(1, 1, n); for(re i=1;i<=m;++i) { int op, x, y, t;char ch; scanf("%lld",&op); if(op == 1) { scanf("%lld",&x); getchar(); printf("%lld\n", getans(x)); } else { scanf("%lld",&x); getchar(); scanf("%c%lld",&ch,&y); if(ch=='+')t=0;else if(ch=='*')t=1;else t=2; modi(1, x, y, t); } } } signed main() { int T; scanf("%lld",&T); for(re h=1;h<=T;++h) { printf("Case #%lld:\n", h); work(); } }
“我不怕千万人阻拦,只怕自己投降”----《倔强》五月天