原来还不知道中国剩余定理能干什么用,先上几篇中国剩余定理的介绍
“中国剩余定理”算理及其应用
“中国剩余定理”算理及其应用:
为什么这样解呢?因为70是5和7的公倍数,且除以3余1。21是3和7的公倍数,且除以5余1。15是3和5的公倍数,且除以7余1。(任何一个一次同余式组,只要根据这个规律求出那几个关键数字,那么这个一次同余式组就不难解出了。)把70、21、15这三个数分别乘以它们的余数,再把三个积加起来是233,符合题意,但不是最小,而105又是3、5、7的最小公倍数,去掉105的倍数,剩下的差就是最小的一个答案。
用歌诀解题容易记忆,但有它的局限性,只能限于用3、5、7三个数去除,用其它的数去除就不行了。后来我国数学家又研究了这个问题,运用了像上面分析的方法那样进行解答。
例 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;
}