题意:给定一个区间,求区间内有多少个合法数(当这个数的二进制中0的个数>=1的个数称为合法数 二进制无前导0)
分析:很明显的数位dp。但因为是跟二进制有关,所以就要在二进制下dp。
用f[i,j,k]表示i位二进制第i位为j时有多少个有k个0的数。f[i,j,k]=f[i-1,j,k-1]+f[i-1,1-j,k-1]{j=0} f[i-1,j,k]+f[i-1,1-j,k]{j=1}
然后就是统计。
先把当前要统计的数x转换成二进制,设x转换成二进制后的位数为len。因为没有前导0,所以第一位肯定是1。然后分两步统计:
1、统计位数小于len且满足条件的数。这个直接循环一遍就好了。
2、统计位数为len且满足条件的数。那么我们从次高位开始统计,同时用一个s0记录以前统计过的位中出现了多少个0.若当前统计的位上是0则跳过,是1则k的范围是(len+1) div 2-s0到i,然后统计就好了
代码:
var i,j,k,x,y:longint; f:array[0..35,0..1,-1..35] of longint; function work(x:longint):longint; var a1,i,j,s0:longint; a:array[1..35] of longint; begin a1:=0; while x>0 do begin inc(a1); a[a1]:=x mod 2; x:=x div 2; end; a[a1+1]:=0; work:=0; for i:=1 to a1-1 do for j:=(i+1) div 2 to i-1 do work:=work+f[i,1,j]; s0:=0; for i:=a1-1 downto 1 do begin if a[i]=1 then for j:=(a1+1) div 2-s0 to i do work:=work+f[i,0,j]; if a[i]=0 then inc(s0); end; end; begin f[0,0,0]:=1; for i:=1 to 32 do for j:=0 to 1 do for k:=0 to i do if j=0 then f[i,j,k]:=f[i-1,0,k-1]+f[i-1,1,k-1] else f[i,j,k]:=f[i-1,0,k]+f[i-1,1,k]; readln(x,y); writeln(work(y+1)-work(x)); end.