hdu 5238 Calculator

某线段树

打模拟赛的时候不知道区间该维护什么东西使其变得可合并,然后就放着了

赛后看了题解才知道这个题维护的是映射关系,感觉也是十分的厉害

考虑一个算式 a+4(mod7)=b

我们发现来 (mod7) 的同余系中,会有以下对应关系

a=0123456b=4560123

其实这就是一个映射

具体的,保存这个映射可以直接开一个数组 a[7],a[i]=(i+4)(mod7)


我们现在已经能把一个算式对应成一个映射了,那么如果我们能计算出两个映射的复合,就可以用线段树解决本题了,不妨设 f(x),g(x) 是我们需要复合的映射

那么复合的结果就是 g(f(x))

:一个 x 过来经过f变成了 c ,然后c进到了 g 里面去出来就是x经过这个复合的映射的结果


理论基础大概就这些,但是如果直接做的话空间复杂度是 O(29393×n×4)

显然是吃不消的

如果能注意到这题的 mod 不是 1e9+7 , 1e9+7 ,而是一个奇怪的数

而且不是素数, 29393=7×13×17×19

四个不相同的素数的乘积,是不是很巧合

聪明的你有没有想到用中国剩余定理来减少运算量呢?

用CRT优化之后,这个题的空间复杂度变成了 O((7+13+17+19)×n×4)

已经可以解决这个问题了o(` • ~ • ′。)o


最后一点是关于代码的

因为需要四个线段树,为了方便我定义了结构体


其它地方如果有不懂的,希望给窝留言,窝会更新一发博客

那么就酱(⊙v⊙)

#include<bits/stdc++.h>
using namespace std;

const int maxn = 51234;

#define root 1,1,n
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define Now int o,int l,int r
#define Mid int m = (l+r)/2

int pmod(int a,int n,int mod){
    a %= mod;
    int ret = 1;
    while(n){
        if(n&1) (ret *= a) %= mod;
        (a *= a) %= mod;
        n>>=1;
    }
    return ret;
}

struct segtree{
    int v[maxn*4][30];
    int mod;
    void clean(int *s){
        for(int i=0;i<mod;i++){
            s[i] = i;
        }
    }
    void init(int val){
        mod = val;
    }
    void add(int o){
        int l = o<<1,r = o<<1|1;
        for(int i=0;i<mod;i++){
            v[o][i] = v[r][ v[l][i] ];
        }
    }
    void update(int o,char oper,int num){ //[+,*,^]
        int *s = v[o];
        if(oper=='+') for(int i=0;i<mod;i++) s[i] = (i+num)%mod;
        if(oper=='*') for(int i=0;i<mod;i++) s[i] = (i*num)%mod;
        if(oper=='^') for(int i=0;i<mod;i++) s[i] = pmod(i,num,mod);
    }
    void update(Now,int pos,char oper,int num){
        if(l==r){
            update(o,oper,num);
            return;
        }
        Mid;
        if(pos <= m) update(lson,pos,oper,num);
        else update(rson,pos,oper,num);
        add(o);
    }
    int query(int x){
        x %= mod;
        return v[1][x];
    }
};

struct allseg{
    segtree sg[4];
    int v[4]={7,13,17,19};
    int coe[4] = {25194,27132,17290,18564};
    int M = 29393;
    allseg(){
        for(int i=0;i<4;i++)
            sg[i].init(v[i]);
    }
    int n;
    void init(int N){
        n = N;
    }
    void update(int pos,char oper,int x){
        for(int i=0;i<4;i++)
            sg[i].update(root,pos,oper,x);
    }
    int query(int x){
        int ans = 0;
        for(int i=0;i<4;i++){
            (ans += coe[i] * sg[i].query(x) )%= M;
        }
        int ret = 0;
        return ans;
    }
};

allseg S;

int getnum(char *s){
    int ret = 0;
    while(*s){
        ret = ret * 10 + (*s-'0');
        s++;
    }
    return ret;
}

int main()
{
    int T,icase = 1;
    scanf("%d",&T);
    int n,m;
    while(T-- && ~scanf("%d %d",&n,&m)){
        S.init(n);
        char op[10];
        for(int i=1;i<=n;i++){
            scanf("%s",op);
            S.update(i,*op,getnum(op+1));
        }
        int ord,x;
        printf("Case #%d:\n",icase++);
        while(m--){
            scanf("%d",&ord);
            if(ord == 1){
                scanf("%d",&x);
                printf("%d\n",S.query(x));
            }
            else{
                scanf("%d",&x);
                scanf("%s",op);
                S.update(x,*op,getnum(op+1));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值