您的位置:首页 > 其它

因数小于16的正整数拆分方案:一个有趣的dp

2011-11-01 21:00 288 查看
“灰常灰常”有趣的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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐