[某ACM比赛]bruteforce

【摘记】

数论题目。ACM。

细节需考虑完整。

【题目描述】

给定N和P,以及F(1)=A,F(2)=B,F(i)=F(i-1)*F(i-2) (i>=3)。求F(n) mod P。1≤n≤1000000000, 1≤P≤1000000, 0≤a, b<1000000

【解题分析】

令S(i)为斐波那契第I项,易得F(i)=A^S(i-2)*B^S(i-1)。暴力显然不能做,由于N比较大,所以考虑优化。首先我们遇到的一个问题是求斐波那契第I项,这可以用矩阵加速做到O(logn)的时间(不考虑精度),可以如果真的要把高精度数算出来,时空复杂度都是明显不够的。我想到了欧拉函数,但是欧拉函数要满足底数和模数互质,这个题目却不一定满足,无法用,于是一直想不出来。

Sol1

P不大,用找循环节的方法在O(P)时间内找出循环长度(易证必定<=P),这样我们就可以把指数缩小到<=P,然后快速幂求解。

对于a和b指数小于1000000,为防止特殊情况,直接做;

有可能a^x mod p=0,需特判

Sol2

公式a^b mod p=a^(b mod φ(p)+φ(p))

(考虑到b<=φ(p) 时,可能a^b mod p>0 但a^(b mod φ(p)+φ(p)) =0,所以b<=φ(p) 需特判)

你懂的,不解释。

共同需要注意的问题:

N很小直接算,避免矩阵加速时出错;

//找循环节
type mat=array[1..2,1..2] of int64;
var t,n,i,j,l,aa,bb,zzz,qq,a2,b2:longint;xx,yy,zz,a,b,p,z,ansa,ansb:int64;
    yuan,tot:mat;
    hash:array[0..1000000] of longint;
function mul(a,b:mat):mat;          {矩阵乘法}
var i,j,k:longint;
begin
	fillchar(mul,sizeof(mul),0);
	for k:=1 to 2 do
	for i:=1 to 2 do
	for j:=1 to 2 do
	mul[i,j]:=(mul[i,j]+a[i,k]*b[k,j] mod Z) mod Z;
end;
function mul2(a,b:mat):mat;
var i,j,k:longint;
begin
        fillchar(mul2,sizeof(mul2),0);
        for k:=1 to 2 do
        for i:=1 to 2 do
        for j:=1 to 1 do
        mul2[i,j]:=(mul2[i,j]+a[i,k]*b[k,j] mod Z) mod Z;
end;


function get(i:int64):mat;   {快速幂}
begin
	if i=1 then get:=yuan
	       else
	       if i=2 then get:=mul(yuan,yuan)
	              else
		      if odd(i) then begin get:=get(i div 2);exit(mul(mul(get,get),yuan));end
		                else begin get:=get(i div 2);exit(mul(get,get));end;
end;
function get_2(i,s:int64):int64;
begin
	if i=0 then exit(1)
	       else
	       if odd(i) then exit(sqr(get_2(i div 2,s)) mod P*s mod P)
	                 else exit(sqr(get_2(i div 2,s)) mod P);
end;
function getlong(s:int64):longint;  {找循环节长度}
var i,j:longint;tmp:int64;
begin
        fillchar(hash,sizeof(hash),0);
        tmp:=1;
	    for i:=1 to maxlongint do
        begin
                tmp:=(tmp*s) mod P;
                if tmp=0 then begin getlong:=-1;exit;end;
		if hash[tmp]<>0 then begin getlong:=i-hash[tmp];exit;end;
		hash[tmp]:=i;
                //writeln(s,' ',tmp);
	end;
end;


begin
assign(input,'bruteforce.in');
assign(output,'bruteforce.out');
reset(input);rewrite(output);
	readln(t);
	for l:=1 to t do
	begin
		readln(a,b,p,n);
                write('Case #',l,': ');
                if n=1 then writeln(a mod P)
                       else
                if n=2 then writeln(b mod P)
                       else
                if n=3 then writeln(a*b mod P)
                       else
                if n=4 then writeln(a*b mod P*b mod P)
                       else
                if n=5 then writeln(a*a mod P*b mod P*b mod P*b mod P)   {小数据直接输出}
                       else
                begin
					qq:=3;xx:=1;yy:=1;
					repeat
					      inc(qq);
					      zz:=yy;
					      yy:=xx+yy;
					      xx:=zz;
					until (qq=n)or(xx>1000000);
					if xx<=1000000 then
                                        //对于a和b指数小于1000000,为防止特殊情况,直接做
					begin
					     writeln(get_2(xx,a)*get_2(yy,b) mod P);
					end
					else
					begin
					aa:=getlong(a);
					bb:=getlong(b);
                                        if aa=-1 then ansa:=0
                                                 else
                                        begin
					        yuan[1,1]:=1;yuan[1,2]:=1;yuan[2,1]:=1;yuan[2,2]:=0;

					        Z:=aa;
					        tot:=get(n-4);
                                                xx:=(tot[1,1]+tot[1,2]) mod Z;
                                                xx:=xx+(1000000 div Z+1)*Z;   {因为循环可能不是从第一个开始的,所以要找后面某一个}
                                                ansa:=get_2(xx,a);
                                        end;
                                        if bb=-1 then ansb:=0
                                                 else
                                        begin
					        Z:=bb;
					        tot:=get(n-3);
                                                xx:=(tot[1,1]+tot[1,2]) mod Z;
                                                xx:=xx+(1000000 div Z+1)*Z;
                                                ansb:=get_2(xx,b);
                                        end;
                                        //writeln(ansa,' ',ansb);
					writeln(ansa*ansb mod P);

					end;
				end;
        end;

close(input);close(output);
end.
//套公式
type mat=array[1..2,1..2] of int64;
var t,n,i,j,l,aa,bb,zzz,qq,a2,b2:longint;q,ph,a,b,p,z,ansa,ansb:int64;
    yuan,tot:mat;
    hash:array[0..1000000] of longint;
function mul(a,b:mat):mat;          {矩阵乘法}
var i,j,k:longint;
begin
	fillchar(mul,sizeof(mul),0);
	for k:=1 to 2 do
	for i:=1 to 2 do
	for j:=1 to 2 do
	mul[i,j]:=(mul[i,j]+a[i,k]*b[k,j] mod Z) mod Z;
end;

function get(i:int64):mat;   {快速幂}
begin
	if i=1 then get:=yuan
	       else
	       if i=2 then get:=mul(yuan,yuan)
	              else
		      if odd(i) then begin get:=get(i div 2);exit(mul(mul(get,get),yuan));end
		                else begin get:=get(i div 2);exit(mul(get,get));end;
end;
function get_2(i,s:int64):int64;
begin
	if i=0 then exit(1)
	       else
	       if odd(i) then exit(sqr(get_2(i div 2,s)) mod P*s mod P)
	                 else exit(sqr(get_2(i div 2,s)) mod P);
end;


begin
assign(input,'bruteforce.in');
assign(output,'bruteforce.out');
reset(input);rewrite(output);

	readln(t);
	for l:=1 to t do
	begin
		readln(a,b,p,n);
                write('Case #',l,': ');
                if n=1 then writeln(a mod P)
                       else
                if n=2 then writeln(b mod P)
                       else
                if n=3 then writeln(a*b mod P)
                       else
                if n=4 then writeln(a*b mod P*b mod P)
                       else
                if n=5 then writeln(a*a mod P*b mod P*b mod P*b mod P)   {小数据直接输出}
                       else
                begin
                     ph:=p;
                     q:=p;
                     for i:=2 to trunc(sqrt(p)) do
                     if q mod i=0 then
                     begin
                          ph:=ph*(i-1) div i;
                          while q mod i=0 do q:=q div i;
                     end;
                     if q>1 then ph:=ph*(q-1) div q;
                     Z:=ph;
                     yuan[1,1]:=1;yuan[1,2]:=1;yuan[2,1]:=1;yuan[2,2]:=0;
                     tot:=get(n-4);
                     if tot[1,1]+tot[1,2]<=ph then ansa:=get_2(tot[1,1]+tot[1,2],a)
                                              else ansa:=get_2((tot[1,1]+tot[1,2]) mod ph+ph,a);
                     tot:=get(n-3);
                     if tot[1,1]+tot[1,2]<=ph then ansb:=get_2(tot[1,1]+tot[1,2],b)
                                              else ansb:=get_2((tot[1,1]+tot[1,2]) mod ph+ph,b);
                     writeln(ansa*ansb mod P);
				end;
        end;

close(input);close(output);
end.
posted @ 2011-08-03 16:20  FancyCoder0  阅读(282)  评论(0编辑  收藏  举报