因數小於16的正整數拆分方案:一個有趣的dp

     

        “灰常灰常”有趣的dp;

        給定一個高精數,將這個高精數分解成若干個不大於16的因數的冪的積,求方案數;

        也就是把s分解爲:a1^K1 * a2^k2 * a3^k3......,其中a<=16;

  

        由於給出數字實在太大了,考場上以爲是個矩乘或母函數(TAT)......

         這道題的dp也算隱藏的比較深;

   

         首先將s分解質因數:可以知道,如果有大於16的質因數,那麼方案數爲0,而如果有11,13的質因數,那麼他們        對答案毫無貢獻,11和13只能單獨存在。

          而剩下的只有2,3,5,7,可以構成的合數有4,6,8,9,10,12,14,15;

          明顯只用2,3可以拼出4,6,8,9,可以枚舉有多少個2和3是拼成4,6,8,9這些數的,此時,如果單獨拼7的個數確定,那麼所有的數都是確定的了(枚舉的2,且已知7,可以推出有多少個14,從而推出10,枚舉3可以推出15的個數,最後推出5的個數)。

          因此可以先dp:用i個2和j個3 一起拼,拼成4,6,8,9的方案數,然後看有多少個7可以單獨放出,統計答案。

貼代碼:

program decomposition;
uses math;
const
    mo=1000000000+9;
type
    arr=array[0..3000]of longint;
var
   s:ansistring; b,a:arr;
   tot,m2,m3:array[0..20]of longint;
   bj:array[0..20]of boolean;
   f:array[0..5000,0..5000]of longint;
   b2,b3,now,k,i,j:longint;
   ans,l,r,tmp:int64;
procedure inf;
begin
   assign(input,'decomposition.in');
   assign(output,'decomposition.out');
   reset(input);rewrite(output);
end;
procedure ouf;
begin
   close(input);close(output);
end;
procedure init;
begin
   readln(s);
   for i:=length(s) downto 1 do
     begin
        inc(a[0]); a[a[0]]:=ord(s[i])-ord('0');
     end;
end;
function arrdiv(x:longint):boolean;
var i:longint;
begin
    move(a[1],b[1],a[0]*4); b[0]:=a[0];
    for i:=b[0] downto 2 do
         b[i-1]:=b[i-1]+(b[i] mod x)*10;
    if b[1] mod x =0 then exit(true) else exit(false);
end;
operator /(var a:arr;b:longint)c:arr;
var i:longint;
begin
    move(a[1],c[1],a[0]*4); c[0]:=a[0];
    for i:=c[0] downto 1 do
      begin
         c[i-1]:=c[i-1]+(c[i] mod b)*10;
         c[i]:=c[i] div b;
      end;
   while (c[c[0]]=0) and (c[0]>1) do dec(c[0]);
end;
procedure work(x:longint);
begin
    while  arrdiv(x) do
      begin
         inc(tot[x]);
         a:=a / x;
      end;
end;
procedure prepare;
begin
     work(2); work(3);work(5);
     work(7); work(11); work(13);
     if (a[0]>1)or ((a[0]=1) and (a[1]>1))
       then begin write(0);    ouf; halt; end;
     for i:=1 to 16 do
        begin
          now:=i;
          while now mod 2=0 do begin now:=now div 2; inc(m2[i]); end;
          while now mod 3=0 do begin now:=now div 3; inc(m3[i]); end;
          if now>1 then bj[i]:=false else bj[i]:=true;
        end;
      fillchar(f,sizeof(f),0);
      f[0,0]:=1;
      for k:=2 to 16 do
        if bj[k] then
         for i:=m2[k] to tot[2] do
           for j:=m3[k] to tot[3] do
            if f[i-m2[k],j-m3[k]]>0 then
              f[i,j]:=(f[i,j]+f[i-m2[k],j-m3[k]]) mod mo;
end;
procedure main;
begin
   for b2:=0 to tot[2] do
     for b3:=0 to tot[3] do
       if f[b2,b3]>0 then
          begin
             l:=max(max(0,tot[7]+b2-tot[2]),tot[7]+b2+b3-tot[2]-tot[3]);
             r:=min(tot[7],tot[5]+tot[7]-tot[2]-tot[3]+b2+b3);
             tmp:=f[b2,b3];
             if r>=l then  ans:=(ans+(r-l+1)*tmp) mod mo;
          end;
  writeln(ans);
end;
begin
   inf;
   init;
   prepare;
   main;
   ouf;
end.                                                                       


        










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章