2015 上海邀请赛c题 calculator hdu5238


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);
			}
		}
	}
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值