zoukankan      html  css  js  c++  java
  • [某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.
    
  • 相关阅读:
    C#中创建Android项目
    C#中创建Android项目
    在C#中获取当前屏幕的分辨率的方法
    数据库面试
    计算机网络面试
    linux面试
    Java使用递归检索文件个数
    二分查找
    富途证券面经(一面挂)
    Mysql
  • 原文地址:https://www.cnblogs.com/oldmanren/p/2126345.html
Copyright © 2011-2022 走看看