• 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
  • 相关阅读:
    201920201学期 20192410《网络空间安全专业导论》第一周学习总结
    201920201学期 20192410《网络空间安全专业导论》第二周学习总结
    Oracle trunc()函数的用法
    20130528
    让ListBox控件支持拖动
    幸福是什么
    实例解析C++/CLI程序进程之间的通讯
    Boost源码剖析之:容器赋值assign
    VC++ MFC 多线程及线程同步
    MFC下窗口分割和文字输出的实现
  • 原文地址:https://www.cnblogs.com/phile/p/4473295.html
Copyright © 2020-2023  润新知