1. 程式人生 > >【數論Day3】進制問題 題解

【數論Day3】進制問題 題解

lin 簡單 得出 tput write input val 第三天 知識

數論進入第三天,進制問題是常用提醒,是數論的一個重要知識點,常考!

題面:http://www.cnblogs.com/ljc20020730/p/6935255.html

1.K進制數(Kbased.pas/c/cpp)

首先明確數據範圍:

【數據規模和約定】

對於40%的數據,a的長度不超過5。

對於100%的數據,a的長度不超過100000。

對於40%暴力枚舉不多說,上代碼:

var t,i,k,tt:longint;
    a:qword;
    s:string;
function pow(x,n:qword):qword;
begin
  if n=0 then exit(1);
  pow:=pow(x,n>>1
); pow:=pow*pow; if n mod 2=1 then pow:=(pow*x); end; function f(s:string):qword; var tx,i:longint; w:array[0..5]of longint; x:qword; begin fillchar(w,sizeof(w),0); w[0]:=length(s); for i:=1 to length(s) do begin if (s[i]<=9)and(s[i]>=0) then w[i]:=ord(s[i])-ord(0) else w[i]:=ord(s[i])-ord(
A)+10; end; x:=0; tx:=w[0]-1; for i:=1 to w[0] do begin x:=x+pow(k,tx)*w[i]; dec(tx); end; exit(x); end; begin assign(input,kbased.in); assign(output,kbased.out); reset(input); rewrite(output); readln(t); for i:=1 to t do begin readln(k); readln(s); a:=f(s); if a mod (k-1)=0 then writeln(
yes) else writeln(no); end; end.

40分非常妥!
但是:對於100%的數據,a的長度不超過100000。
怎麽處理?
思考一個問題:每一個k進制數可以表示為什麽?

任何一個k進制正整數都可以寫成:a0*k0+a1*k1+…+an*kn

這是初賽的內容吧,相信來看題解的人這點心中自然明確。

對於幾乎要達到線性的時間復雜度來說,必須要從Kn這裏下手!

那麽來看一個非常簡單的例子:

∵k0=1∴ k0 mod (k-1) = 1

∵k1=k-1+1∴ k1 mod (k-1) = 1

∵k2 = k*k∴ k2 mod (k-1) = 1

……

所以推出:kn = kn-1*kkn mod (k-1) = 1

沒問題吧?

●任何一個k進制正整數都可以寫成:a0*k0+a1*k1+…+an*kn

又因為所有的k?k-1都是1(?是一個常數)

所以任何一個k進制正整數,模k-1的結果,都等於它各個數位上數字之和模k-1

任何一個k進制正整數都可以寫成:a0*k0+a1*k1+…+an*kn

這是初賽的內容吧,相信來看題解的人這點心中自然明確。

對於幾乎要達到線性的時間復雜度來說,必須要從Kn這裏下手!

那麽來看一個非常簡單的例子:

∵k0=1∴ k0 mod (k-1) = 1

∵k1=k-1+1∴ k1 mod (k-1) = 1

∵k2 = k*k∴ k2 mod (k-1) = 1

……

所以推出:kn = kn-1*kkn mod (k-1) = 1

沒問題吧?

●任何一個k進制正整數都可以寫成:a0*k0+a1*k1+…+an*kn

又因為所有的k?k-1都是1(?是一個常數)

所以任何一個k進制正整數,模k-1的結果,都等於它各個數位上數字之和模k-1

【程序】

var  w:array[0..100000]of longint;
     t,i,k:longint;
     s:ansistring;
     a:qword;
function f(s:ansistring):qword;
var i:longint;
    ans:qword;
begin
 fillchar(w,sizeof(w),0);
 w[0]:=length(s);
 ans:=0;
 for i:=1 to length(s) do begin
  if (s[i]<=9)and(s[i]>=0) then
  w[i]:=ord(s[i])-ord(0)
  else w[i]:=ord(s[i])-ord(A)+10;
  inc(ans,w[i]);
 end;
 exit(ans);
end;
begin
assign(input,kbased.in);
assign(output,kbased.out);
reset(input);
rewrite(output);
 readln(t);
 for i:=1 to t do begin
  readln(k);
  readln(s);
  fillchar(w,sizeof(w),0);
  a:=f(s);
  if a mod (k-1)=0 then writeln(yes)
  else writeln(no);
 end;
 close(input);
 close(output);
end.

2.C and.pas/c/cpp

暴力出奇跡:

20%N<=1000

另外20%:只有01

不解釋O(n^2)

非暴力出滿分:

【樣例分析】

And去運算:0 and 0=0 0 and 1=0 1 and 0=0 1 and 1=1

1與第2

1與第3

2與第3

舉例:5 and 23

1 and 2

1 and 1

2 and 1

00101

and 10111

00101

01

and 10

01

and 01

10

and 01

00

01

00

5

另外幾個數字!最大數為1 and 1=1

舉例:4個數11 6 10 8

1011

0110

1010

1000

看出來了吧?

1 2 4得出的數最大!

所以,可以用貪心來解決:為使兩數的and最大,故從高位開始(從左向右a[]<=109:31位到0位)枚舉,看第1列的1個數有否大於等於2個;若找到,則將其它第1列為0的設為不可用(vis[MAX_N])。繼續找後面位也同樣處理。

取第i位是否為1a[j]>>i mod 2=1。同理第i位是否為1: a[j]>>i mod 2=0;

答案為ans:=ans+(1<<i);

var n,i,j,ans,cnt:longint;
    a:array[1..100000]of longint;
    u:array[1..100000]of boolean;
begin
assign(input,and.in);
assign(output,and.out);
reset(input);
rewrite(output);
 readln(n);
 fillchar(u,sizeof(u),true);
 for i:=1 to n do read(a[i]);
 for i:=31 downto 0 do begin
  cnt:=0;
  for j:=1 to n do begin
  if (a[j]>>i mod 2=1)and(u[j]) then inc(cnt);
  end;
  if cnt>=2 then begin
    ans:=ans+(1<<i);
    for j:=1 to n do
     if a[j]>>i mod 2=0 then u[j]:=false;
  end;
 end;
 writeln(ans);
 close(input);
 close(output);
end.

【數論Day3】進制問題 題解