POJ 1995 Raising Modulo Numbers 快速幂取模

http://poj.org/problem?id=1995

 

Raising Modulo Numbers
Time Limit: 1000MS   Memory Limit: 30000K

Description

People are different. Some secretly read magazines full of interesting girls' pictures, others create an A-bomb in their cellar, others like using Windows, and some like difficult mathematical games. Latest marketing research shows, that this market segment was so far underestimated and that there is lack of such games. This kind of game was thus included into the KOKODáKH. The rules follow: 

Each player chooses two numbers Ai and Bi and writes them on a slip of paper. Others cannot see the numbers. In a given moment all players show their numbers to the others. The goal is to determine the sum of all expressions Ai Bi from all players including oneself and determine the remainder after division by a given number M. The winner is the one who first determines the correct result. According to the players' experience it is possible to increase the difficulty by choosing higher numbers. 

You should write a program that calculates the result and is able to find out who won the game. 

Input

The input consists of Z assignments. The number of them is given by the single positive integer Z appearing on the first line of input. Then the assignements follow. Each assignement begins with line containing an integer M (1 <= M <= 45000). The sum will be divided by this number. Next line contains number of players H (1 <= H <= 45000). Next exactly H lines follow. On each line, there are exactly two numbers Ai and Bi separated by space. Both numbers cannot be equal zero at the same time.

Output

For each assingnement there is the only one line of output. On this line, there is a number, the result of expression 

(A1B1+A2B2+ ... +AHBH)mod M.

Sample Input

3
16
4
2 3
3 4
4 5
5 6
36123
1
2374859 3029382
17
1
3 18132

Sample Output

2
13195
13
求a^b%c(这就是著名的RSA公钥的加密方法)
当a,b很大时,直接求解这个问题不太可能
你能想到哪些优化呢?
算法1:直观上,也许最容易想到的是利用a*b%c=((a%c)*b)%c,这样每一步都进行这种处理,这就解决了a^b可能太大存不下的问题,但这个算法的时间复杂度依然是O(n),根本没有得到优化。当b很大时运行时间会很长
算法2:另一种算法利用了二分的思想,可以达到O(logn)。
可以把b按二进制展开为b=p(n)*2^n+p(n-1)*2^(n-1)+…+p(1)*2+p(0)
其中p(i) (0<=i<=n)为0或1
这样a^b=a^(p(n)*2^n+p(n-1)*2^(n-1)+...+p(1)*2+p(0))
=a^(p(n)*2^n)*a^(p(n-1)*2^(n-1))*...*a^(p(1)*2)*a^p(0)
对于p(i)=0的情况,a^p(i)*2^(i-1)=a^0=1,不用处理
我们要考虑的仅仅是p(i)=1的情况
a^(2^i)=(a^(p(i)*2(i-1)))^2
利用这一点,我们可以递推地算出所有的a^(2^i)
当然由算法1的结论,我们加上取模运算a^(2^i)%c=((a^(2(i-1))%c)*a^(2(i-1)))%c
于是再把所有满足p(i)=1的a^(2^i)%c按照算法1乘起来再%c就是结果
即二进制扫描从最高位一直扫描到最低位。

模板:

int modexp(long long a,long long b,long long n)
{
    int ret=1;
    long long tmp=a;
    while(b)
    {
       //基数存在
       if(b&0x1) ret=ret*tmp%n;
       tmp=tmp*tmp%n;
       b>>=1;
    }
    return ret;
}
/* Author : yan
 * Question : POJ 1995 Raising Modulo Numbers
 * Data && Time : Wednesday, January 19 2011 12:40 PM
 * Compiler : gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
*/
#include<stdio.h>

int modexp(long long a,long long b,long long n)
{
    int ret=1;
    long long tmp=a;
    while(b)
    {
       //基数存在
       if(b&0x1) ret=ret*tmp%n;
       tmp=tmp*tmp%n;
       b>>=1;
    }
    return ret;
}

int main()
{
	//freopen("input","r",stdin);
	int z;
	int m;
	int ans;
	int h;
	int a,b;
	scanf("%d",&z);
	while(z--)
	{
		ans=0;
		scanf("%d",&m);
		scanf("%d",&h);
		while(h--)
		{
			scanf("%d %d",&a,&b);
			ans+=modexp(a,b,m);
			ans%=m;
		}
		printf("%d/n",ans);
	}
	return 0;
}
 

 

你可能感兴趣的:(POJ 1995 Raising Modulo Numbers 快速幂取模)