地址:http://codeforces.com/contest/215/problem/E
思路:通過確定第一節來找週期數,這題核心在於去重。
例如以2個二進制數爲一小節生成的週期數11 11 11 11、10 10 10 10與以4個二進制數爲一小節生成的週期數1111 1111、1010 1010重複。
自己試了多種方法,但每次都不能很好的將置頂情況與不置頂情況分開。
看了下別人的思路,每次都重置數組來去重,略犀利。
代碼:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL __int64
LL num[100],dp[100];
LL getdp(int len,int k,LL m) //對於置頂情況的處理
{
LL x=0;
for(int i=0;i<k;i++) x<<=1,x+=num[len-i];
LL y=x;
for(int i=1;i<len/k;i++) y<<=k,y+=x; //這裏y是把極值生成了
return x-(1<<(k-1))+1-(y>m); //將極限值與限定值相比較,從而判斷極值是否在限定範圍內
}
LL getans(LL m)
{
int len;
LL n=m,ans=0;
for(len=0;n;n>>=1) num[++len]=n&1;
for(int i=2;i<=len;i++)
{
memset(dp,0,sizeof(dp)); //每次重置數組,這樣可以對付多種情況。我做這題時只想到了開二進制來去重,沒想到滾動更方便
for(int j=1;j<i;j++)
{
if(i%j) continue;
if(i<len) dp[j]=1<<(j-1);
else dp[j]=getdp(len,j,m);
for(int k=1;k<j;k++) //這裏去重
if(!(j%k)) dp[j]-=dp[k];
ans+=dp[j];
}
}
return ans;
}
int main()
{
LL l,r;
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",getans(r)-getans(l-1));
return 0;
}