热身训练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();
    }
}

 

 

“我不怕千万人阻拦,只怕自己投降”----《倔强》五月天

 

posted @ 2021-07-19 18:42  kzsn  阅读(53)  评论(0)    收藏  举报