acm fzu 2108 Mod problem

Accept: 17    Submit: 50
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

Given one non-negative integer A and one positive integer B, it’s very easy for us to calculate A Mod B. Here A Mod B means the remainder of the answer after A is divided by B. For example, 7 Mod 5 = 2, 12 Mod 3 = 0, 0 Mod 3 = 0.

In this problem, we use the following rules to express A.

(1) One non-empty string that only contains {0,1,2,3,4,5,6,7,8,9} is valid.

For example, 123, 000213, 99213. (Leading zeros is OK in this problem)

(2) If w is valid, then [w]x if valid. Here x is one integer that 0<x<10.

For example, [012]2=012012, [35]3[7]1=3535357.

(3) If w and v are valid, then wv is valid.

For example, w=[231]2 and v=1, then wv=[231]21 is valid (which is 2312311).

Now you are given A and B. Here A is express as the rules above and B is simply one integer, you are expected to output the A Mod B.

 Input

The first line of the input contains an integer T(T≤10), indicating the number of test cases.

Then T cases, for any case, only two lines.

The first line is one non-empty and valid string that expresses A, the length of the string is no more than 1,000.

The second line is one integer B(0<B<2,000,000,000).

You may assume that the length of number A in decimal notation will less than 2^63.

 Output

For each test case, output A Mod B in a single line.

 Sample Input

3[0]9[[1]2]310007[[213231414343214231]5]110007[0012]11

 Sample Output

103439430

 Source

“高教社杯”第三届福建省大学生程序设计竞赛
哼,我终于A了,这是福大武核出的题,我在贴吧问这道题时,他说建树然后遍历一遍就OK。。。但他不说怎么构树,说了等于没说


但这不能阻止我AC,哼,说白了就是快速取模

快速模取幂

    数论计算中经常出现的一种运算就是求一个数的幂ab对另外一个数n个模的运算,即计算:

ab mod n (a,b,n是正整数)

    由于计算机只能表示有限位的整数,所以编程时模取幂的运算要注意值的大小范围,当ab的值超过整数范围时,mod运算便无法进行。

    如何解决这个问题,我们引出一个能计算ab mod n的值的有用算法——反复平方法,首先我们必须明确:

d=ab mod n=(…((((a mod n)*a)mod n)*a)mod n…*a)mod n    {共b个a}

    由此可以引出一个迭代式

         d:=a;

         for i:=2 to b do

            d:=d mod n*a;

         d:=d mod n;

    时间复杂度为O(b),当b很大时,效率很低。我们可以将b转换为二进制数<bk,bk-1,...,b1,b0>,然后从最低位b0开始,由右至左逐位扫描,每次迭代时,用到下面两个恒等式:

a2c mod n =(ac)2 mod n      bi=0            

a2c+1 mod n =a*(ac)2 mod n   bi=1 (0<=c<=b)

    其中c为b的二进制数的后缀(bi-1...b0)对应的十进制数,当c成倍增加时,算法保持d=ac mod n不变,直至c=b。

     程序实现可如下:

long long result(long long a,long long b,long long m)
{
    long long d,t;

    d=1;
    t=a;
    while (b>0)
    {
        if (b%2==1)
            d=(d*t)%m;
        b/=2;
        t=(t*t)%m;
    }

    return d;
}

/*
快速幂
int pow(int a ,int k)
{
    int rec = 1;
    while( k )
    {
        if (k & 1)
            rec *= a;
        a *= a;
        k >>= 1;
    }
    return rec;
}
  & 表示的是k这一位是否为一,K>>= 表示K的位置右移以为,while(K)是直到K移到最后一位的时候弹出.
举个例子,3^13;13=1*2^3+1*2^2+0*2^1+1*2^0;那么第一次K=13;这个时候K&1表示探查2^0的这一位是否为1;为1,则进行rec*=a; k>>=1表示将13右移一位,即13/2;可以在之前的13的上面直接表示出来.6=1*2^2+1*2^1+0*2^0;这个时候,k&1则为0了。因为2^0的系数是0;
快速幂取模
int ModPow(int a,int b,int n)
{
    int rec=1;
    while(b)
    {
        if (b & 1)
            rec = (rec * a) % n;
        a = (a * a) % n;
        b >>= 1;
    }
    return rec % n;
}
模运算规则:
模运算与基本四则运算有些相似,但是除法例外。其规则如下:
(a + b) % p = (a % p + b % p) % p
(a – b) % p = (a % p – b % p) % p
(a * b) % p = (a % p * b % p) % p
ab % p = ((a % p)b) % p
结合率:
((a+b) % p + c) % p = (a + (b+c) % p) % p
((a*b) % p * c)% p = (a * (b*c) % p) % p
*/

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 1010
char s[N];
long long m;
struct node{
	long long v,k;//位数
};
long long qmod(long long a,long long k){//快速取模
	long long v=1;
	//while(--k)v=(v*10)%m;
	while(k){
		if(k&1)v=(v*a)%m;
		a=(a*a)%m;
		k>>=1;
	}
return v%m;
}
node build(int l,int r){
	node p={0},t;
	int i,flag=0;
	int x,y,k;
	for(i=l;i<=r;i++){
		if(s[i]=='['){if(flag==0)x=i+1;flag++;}
		else if(s[i]==']'){
			flag--;
			if(flag==0){//找到最外的[]
				y=i-1;i++;k=s[i]-'0';
				t=build(x,y);//得到[]里的位数m,和取模后的v
				//cout<< "【"<<k<<"】"<<x<<" "<<y<<endl;
				long long h=qmod(10,t.k);
				long long g=1,e=h;
				//cout<<t.k<<" "<<h<<endl;
				p.k+=k*t.k;//总位数加上
				p.v=(p.v*qmod(h,k))%m;//p.v*(10^t.k)
				//t.v=qmod(t.v,k);//t.v^k************
				while(--k){
					g+=e;
					e=(e*h)%m;
					if(g>=m)g%=m;
				}
				//cout<<t.v<<"-----"<<g<<endl;
				t.v=(t.v*g)%m;
				p.v=(p.v+t.v)%m;
				//cout<<p.v<<"*****"<<p.k<<endl;
			}
		}
		else if(flag==0){//数字
			p.v=p.v*10+s[i]-'0';
			if(p.v>=m)p.v%=m;
			p.k++;//位数加1
		}
	}
	return p;
}
int main(){
	int T;
	//m=7;printf("%lld\n",qmod(10,2));
	scanf("%d",&T);
	while(T--){
		scanf("%s%lld",s,&m);
		printf("%lld\n",build(0,strlen(s)-1).v);
	}

return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值