hdu - 5238 Calculator(线段树+中国剩余定理)线段树好题

原来还不知道中国剩余定理能干什么用,先上几篇中国剩余定理的介绍

下面内容转自:http://wenku.baidu.com/link?url=g1Hiu6UtSoOR6Y3tiHpn5M3_HPnuoTN_xpm1GPFHAkZB-nW0m61vunMmNIqxacAke3jPmMvE_M4gWGd25D0C1ZthZTuzx-u1DHSbunP8qrS

中国剩余定理算理及其应用

中国剩余定理算理及其应用:

 
为什么这样解呢?因为7057的公倍数,且除以312137的公倍数,且除以511535的公倍数,且除以71。(任何一个一次同余式组,只要根据这个规律求出那几个关键数字,那么这个一次同余式组就不难解出了。)把702115这三个数分别乘以它们的余数,再把三个积加起来是233,符合题意,但不是最小,而105又是357的最小公倍数,去掉105的倍数,剩下的差就是最小的一个答案。
用歌诀解题容易记忆,但有它的局限性,只能限于用357三个数去除,用其它的数去除就不行了。后来我国数学家又研究了这个问题,运用了像上面分析的方法那样进行解答。

     

1 :一个数被 3 除余 1 ,被 4 除余 2 ,被 5 除余 4 ,这个数最小是几?
题中 3 4 5 三个数两两互质。
则〔 4 5 =20 ;〔 3 5 =15 ;〔 3 4 =12 ;〔 3 4 5 =60
为了使 20 3 除余 1 ,用 20×2=40
使 15 4 除余 1 ,用 15×3=45
使 12 5 除余 1 ,用 12×3=36
然后, 40×1 45×2 36×4=274
因为, 274>60 ,所以, 274 60×4=34 ,就是所求的数。

2 :一个数被 3 除余 2 ,被 7 除余 4 ,被 8 除余 5 ,这个数最小是几?
题中 3 7 8 三个数两两互质。
则〔 7 8 =56 ;〔 3 8 =24 ;〔 3 7 =21 ;〔 3 7 8 =168
为了使 56 3 除余 1 ,用 56×2=112
使 24 7 除余 1 ,用 24×5=120
使 21 8 除余 1 ,用 21×5=105
然后, 112×2 120×4 105×5=1229
因为, 1229>168 ,所以, 1229 168×7=53 ,就是所求的数。

3 :一个数除以 5 4 ,除以 8 3 ,除以 11 2 ,求满足条件的最小的自然数。
题中 5 8 11 三个数两两互质。
则〔 8 11 =88 ;〔 5 11 =55 ;〔 5 8 =40 ;〔 5 8 11 =440
为了使 88 5 除余 1 ,用 88×2=176
使 55 8 除余 1 ,用 55×7=385
使 40 11 除余 1 ,用 40×8=320
然后, 176×4 385×3 320×2=2499
因为, 2499>440 ,所以, 2499 440×5=299 ,就是所求的数。

那么根据上面的例子就可以看出中国剩余定理的用处了,下面说一下这个题的做法

题意给出一个计算的序列,包含{+,*,^}三种运算。给出两种操作,1是给出初始值,求按照序列计算的答案,2是修改序列中某个位置的数和运算符。

29393可以分解成7*13*17*19,那么就可以维护模这几个素数的答案,然后用中国剩余定理合并出答案

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=50010;
const int MOD=29393;
int mod[4]={7,13,17,19};
int p[4][20][MOD];

int N,M;
void init()
{
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<mod[i];j++)
        {
            p[i][j][0]=1;
            for(int k=1;k<MOD;k++)
                p[i][j][k]=p[i][j][k-1]*j%mod[i];
        }
    }
}
struct IntervalTree
{
    int f[maxn<<2][4][20];
    void build(int o,int l,int r)
    {
        if(l==r)
        {
            char c;
            int x;
            scanf("%c%d",&c,&x);
            getchar();
            maintain(o,c,x);
            return;
        }
        int mid=(l+r)>>1;
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
        pushup(o);
    }
    void maintain(int o,char op,int x)
    {
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<mod[i];j++)
            {
                if(op=='+')f[o][i][j]=(j+x)%mod[i];
                else if(op=='*')f[o][i][j]=(j*x)%mod[i];
                else f[o][i][j]=p[i][j][x];
            }
        }
    }
    void pushup(int o)
    {
        for(int i=0;i<4;i++)
        {
            for(int j=0;j<mod[i];j++)
                f[o][i][j]=f[o<<1|1][i][f[o<<1][i][j]];
        }
    }
    void update(int o,int l,int r,int x,int c,int v)
    {
        if(l==r)
        {
            maintain(o,c,v);
            return ;
        }
        int mid=(l+r)>>1;
        if(x<=mid)update(o<<1,l,mid,x,c,v);
        else update(o<<1|1,mid+1,r,x,c,v);
        pushup(o);
    }
}tree;
int pow_mul(int x,int n,int m)
{
    int res=1;
    while(n)
    {
        if(n&1)res=(res*x)%m;
        x=(x*x)%m;
        n>>=1;
    }
    return res;
}
int cal(int x)
{
    int ans=0;
    for(int i=0;i<4;i++)
    {
        int Mi=MOD/mod[i];
        int t=pow_mul(Mi,mod[i]-2,mod[i]);
        ans=(ans+tree.f[1][i][x%mod[i]]*Mi*(t%mod[i]))%MOD;
    }
    return (ans+MOD)%MOD;
}
void solve()
{
    int op,x,v;
    char c;
    scanf("%d%d",&N,&M);
    getchar();
    tree.build(1,1,N);
    while(M--)
    {
        scanf("%d%d",&op,&x);
        getchar();
        if(op==1)printf("%d\n",cal(x));
        else
        {
            scanf("%c%d",&c,&v);
            tree.update(1,1,N,x,c,v);
        }
    }
}
int main()
{
    int T,cas=1;
    init();
    scanf("%d",&T);
    while(T--)
    {
        printf("Case #%d:\n",cas++);
        solve();
    }
    return 0;
}




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值