題意:給你一個區間[l,r],求這個區間內滿足條件的數,條件是:這個數的二進制表示時,dig[i] == dig[i+k],(0<k<len,且len%k==0,len爲這個數的二進制代碼長度)
思路:考慮[0,x]這個區間,若x的位數爲len,當數的長度 i 爲0~len-1時,則是無限制的,這時dp[i] = sum{2^(k-1)},k爲滿足條件的循環長度。而且還要去掉重複的,比如當長度爲6時,循環長度爲2,3的數均會重複計算,當數的長度爲len時,則在限制下,計算滿足條件的數,具體實現看代碼註釋:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 600005
#define N 1000005
#define max_inf 0x7f7f7f7f
#define min_inf 0x80808080
#define mod 1000000007
#define lc rt<<1
#define rc rt<<1|1
ll dp[70] , r , l , table[70];//table[i] = 2^i;
int dig[70];
ll Cal(int k)//計算長度爲k的數,滿足條件的個數
{
int i , j;
ll ret = 0;
for (i = 1 ; i < k ; i++)
{
if (k%i)continue;
dp[i] = table[i-1];
for (j = 1 ; j < i ; j++)//減掉重複計算的數
{
if (i%j == 0)
dp[i] -= dp[j];
}
ret += dp[i];
}
return ret;
}
ll Solve(ll k)
{
int i , j , len = 0;
ll ret = 0 , temp = k , num;
while (temp)
{
dig[++len] = temp&1;
temp >>= 1;
}
//計算無限制時滿足條件的數
for (i = 1 ; i < len ; i++)ret += Cal(i);
for (i = 1 ; i < len ; i++)//長度爲len時,枚舉循環長度
{
if (len%i)continue;
num = 1;
temp = 0;
dp[i] = 0;
for (j = len-1 ; j > len-i ; j--)
{
//若dig[j]==1則可以令dig[j]=0,轉變成無限制的情況
if (dig[j])dp[i] += table[i-(len-j)-1];
num = num*2+dig[j];
}
temp = num;
int up = len/i;
for (j = 1 ; j < up ; j++)num = (num<<i)+temp;
//num保存的爲循環長度爲i,且循環內每一位都受限制的情況下的這個數
dp[i] += (num <= k);//若num比k小,則加入答案中
//去掉重複的計算的數
for (j = 1 ; j < i ; j++)
{
if (i%j == 0)
dp[i] -= dp[j];
}
ret += dp[i];
}
return ret;
}
int main()
{
int i;
for (table[0] = 1 , i = 1 ; i < 70 ; i++)table[i] = table[i-1]*2;
while (~scanf("%I64d%I64d",&l,&r))
printf("%I64d\n",Solve(r)-Solve(l-1));
return 0;
}