poj3252

好了,我的数论渣爆了…………

首先[n,m]内的round number显然就是f[m]-f[n-1]

即问0~x内有多少round number;

设x的二进制位数为t;

首先很好分析出在这个范围

若某数的二进制位数<t,则首位1不动,后面组合即可;

然后被卡在当二进制位数为t的round number有多少这招情况;

后来看了别人的解题报告才恍然大悟;

对于x,不算首位的1,只要把当前某一个1改成0,并对后面位数重新01组合就一定小于x,

于是

对于x的每一位1,对后面重新组合即可

显然最终答案不会爆maxlongint

现在想想,其实就是数位dp的思想

 1 var c:array[0..40,0..40] of longint;
 2     a:array[0..40] of longint;
 3     n,m:longint;
 4 procedure prepare;  //预处理组合数
 5   var i,j:longint;
 6   begin
 7     c[0,0]:=1;
 8     for i:=1 to 31 do
 9     begin
10       c[i,0]:=1;
11       c[i,i]:=1;
12       for j:=1 to i-1 do
13         c[i,j]:=c[i-1,j]+c[i-1,j-1];
14     end;
15   end;
16 
17 function count(x:longint):longint;
18   var p,t,i,j,q,s1,s0:longint;
19   begin
20     fillchar(a,sizeof(a),0);
21     if x=0 then exit(1);
22     p:=x;
23     t:=0;
24     while p<>0 do                  //十转二
25     begin
26       t:=t+1;
27       a[t]:=p mod 2;
28       p:=p shr 1;
29     end;
30     count:=1;                     //考虑0
31     for i:=2 to t-1 do            //当二进制位数小于t
32     begin
33       if i mod 2=1 then q:=(i-1) div 2+1    //保证0比1多
34       else q:=i div 2;
35       for j:=q to i-1 do
36         count:=count+c[i-1,j];
37     end;
38     s0:=0;
39     s1:=0;
40     for i:=1 to t do              //先特判x是否是round number    
41       if a[i]=1 then inc(s1) else inc(s0);
42     if s0>=s1 then count:=count+1;
43     s0:=0;
44     s1:=1;
45     for i:=t-1 downto 1 do      
46       if a[i]=1 then
47       begin
48         for j:=i-1 downto 0 do       //j表示后面可能出现0的个数
49           if j+s0+1>=i-1-j+s1 then  //保证0比1多,i-1表示当前位1后面的,+1表示将这位1变成0后后面重新组合
50             count:=count+c[i-1,j]
51           else break;
52         inc(s1);                   //别忘统计前面的0,1个数,后面的排列情况是由起决定的
53       end
54       else inc(s0);
55   end;
56 begin
57   readln(n,m);
58   prepare;
59   writeln(count(m)-count(n-1));      //经常用到的转化思想
60 end.
View Code
原文地址:https://www.cnblogs.com/phile/p/4473295.html