因数小于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可以单独放出,统计答案。
贴代码:
给定一个高精数,将这个高精数分解成若干个不大于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.
相关文章推荐
- 自守数 寻找出2千万以内的所有自守数。注意,2千万的平方已经超出了整数表达的最大范围,所以该程序使用了一个巧妙的方案。
- 如何用Java编写从命令行输入一个整数N,当N小于或等于5则求1+2+3+
- 16、正整数序列Q中的每个元素都至少能被正整数a和b中的一个整除,现给定a和b, 需要计算出Q中的前几项
- 求一个小于10的正整数的n次方,n很大
- dp(整数拆分 uva10313 - Pay the Price)
- 问题十八: 一个整数(小于100000),它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多?
- 给一个小于10位的正整数以左补0的方式补满10位
- 求一个整数的因数
- BZOJ 2173: 整数的lqp拆分( dp )
- 定义一个整数N,不用本地变量和循环,输入N,依次判断2N,4N,8N...,一旦大于5000,则倒序输出小于5000的那些数(...,8N,4N,2N,N)
- 将202413.9拆分成一个两位小数的数字和一个2000--3000左右整数的乘积
- 一道笔试题 题目是这样的:判断一个小于1000的正整数是否为素数。
- 小于等于n的正整数相加等于m的一个算法
- 杭电ACM OJ 1013 Digital Roots 如何用递归优雅地把一个未知长度的长整数的每一位拆分出来
- 面试题总结16 对一个整数开根号
- 将一个整数拆分成不相同的正整数之和,打印出所有的组合
- poj 2948 比较好的dp,方向不是一个的最优方案dp,可以作为以后出题,如果改成4个方向的怎么解决?
- bzoj 2173 整数的lqp拆分 | dp | 找规律
- Java:求整数位数:任意输入一个整数(小于10位),求它的位数
- 将一个数拆分成整数部分和小数部分