您的位置:首页 > 其它

2015 上海邀请赛c题 calculator hdu5238

2015-05-29 19:38 351 查看
http://acm.hdu.edu.cn/showproblem.php?pid=5238

题目很不错,注意到29393=7*13*17*19,只要计算出答案模这四个数的值即可通过中国剩余定理解出答案。

那么如何高效求解这四个值呢?

可以用一个映射cha[i][j]表示,i表示mod的值是第i种,j表示对这段区间而言的初始长度。

显然tree[x].cha[j][i]=tree[lr(x)].cha[j][tree[ll(x)].cha[j][i]];

细节都在代码里:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
using namespace std;

const int mod[4]={7,13,17,19},Mod=29393;    //四个mod值
int an[4];           //储存四个分答案

inline int ll(int a) {
return 2*a;
}
inline int lr(int a) {
return 2*a+1;
}
inline int pow (int m,int a,int b) {                //对第m个数求模是a^b
int ss=1,i=1;
while (i<=b) {
if(b&i)
ss=a*ss%mod[m];
i=i<<1;
a=a*a%mod[m];
}
return ss;
}
struct node {
int cha[4][19];
};
node tree [200005];
void pushup (int x) {                    //维护节点x
int i,j;
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[x].cha[j][i]=tree[lr(x)].cha[j][tree[ll(x)].cha[j][i]];
}
char ope[50005];               //表示初始输入的符号
int val[50005];                //初始数值

void build (int st,int l,int r) {                //建树
if(l==r) {
int i,j;
if(ope[l]=='+') {
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[st].cha[j][i]=(i+val[l])%mod[j];
}
else if(ope[l]=='*') {
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[st].cha[j][i]=(i*val[l])%mod[j];
}
else if(ope[l]=='^') {
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[st].cha[j][i]=pow(j,i,val[l]);
}
return ;
}
int mid=(l+r)/2;
build (ll(st),l,mid);
build (lr(st),mid+1,r);
pushup(st);
return ;
}

void gcd (int a,int b,int &d,int& x,int &y) {                //扩展欧几里得
if(!b) { d=a; x=1; y=0; }
else { gcd(b,a%b,d,y,x); y-=x*(a/b); }
}

int get_ans (int ini) {
int i,a,w,d,y,x=0;
for(i=0;i<=3;i++) {
a=ini%mod[i];
an[i]=tree[1].cha[i][a];
//		cout<<"ans["<<"i"<<"]:"<<an[i]<<endl;
}
for(i=0;i<=3;i++) {
w=Mod/mod[i];
gcd(mod[i],w,d,d,y);
x=(x+y*w*an[i])%Mod;
}
return (x+Mod)%Mod;
}

void change (int now,int l,int r,int pos,char oo,int va) {              //修改pos位置,now是当前考虑节点的标号,l,r是这个节点的左右界,oo是将要修改为的
int i,j;                                                             //符号,va是值
if (l==r) {
if(oo=='+') {
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[now].cha[j][i]=(i+va)%mod[j];
}
else if(oo=='*') {
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[now].cha[j][i]=(i*va)%mod[j];
}
else if(oo=='^') {
for(j=0;j<=3;j++)
for(i=0;i<mod[j];i++)
tree[now].cha[j][i]=pow(j,i,va)%mod[j];
}
return ;
}
int mid=(l+r)/2;
if(pos<=mid)
change(ll(now),l,mid,pos,oo,va);
else
change(lr(now),mid+1,r,pos,oo,va);
pushup(now);
return ;
}

int main ()
{
//	freopen("aa.txt","r",stdin);
int T,n,m,cas,i,j,ini,pos,va,ans;
char oo;
scanf("%d",&T);
for(cas=1;cas<=T;cas++) {
printf("Case #%d:\n",cas);
scanf("%d%d",&n,&m);
getchar();
for(i=1;i<=n;i++) {
scanf("%c%d",ope+i,val+i);
getchar();
}
build (1,1,n);
for(i=1;i<=m;i++) {
scanf("%d",&j);
if(j==1) {
scanf("%d",&ini);
ans=get_ans(ini);
printf("%d\n",ans);
}
else {
scanf("%d %c%d",&pos,&oo,&va);
change(1,1,n,pos,oo,va);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: