HDU-5238 Calculator(线段树+中国剩余定理)

本文介绍了一种处理特定数学表达式计算的高效算法。该算法利用了质因数分解、中国剩余定理(CRT)及线段树等技术,解决了大规模数据处理下的运算问题,实现了快速求解和更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给定一个关于 xx 的表达式,形如下例:
x×4+2^3+8×6
按如下方法计算:
(((x×4)+2)3+8)×6(((x×4)+2)3+8)×6
运算符只有 ++ 加号、 × 乘号、 ^^ 幂运算三种,给定的式子中有 nn 次运算。
进行 m 次给定的操作:操作一表示对一个给定的 xx 值,求原式的结果并对 29393 取模;操作二表示将 kk 位置上的运算符和运算符后面的数字改变。完成这些操作。
1n,m50000

思路

本题数据范围较大,朴素的 O(nm)O(nm) 算法肯定会超时。
2939329393 看起来很奇怪,对它进行质因数分解,发现能拆成 7×13×17×197×13×17×19 。那只用算出对于小数字取模的结果,再用 CRTCRT 合并即可。
由于 xx 固定在算式的最前端,更改也只是对于一个确定的位置,我们可以尝试处理出区间进行合并,把算式中的部分划分成区间,对,就是线段树。
我们对于每一个质因子构造出一棵线段树,总共四棵线段树,每棵线段树中保存的信息是一个数组 mp ,其中 mpimpi 表示这个区间接收到最前端的数位 ii 时的运算结果,对于这棵树的模数 P 取模,则 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值