CF215E Periodical Numbers
我们枚举数的长度 \(len\),循环节长度 \(k\) ,算出范围内有多少个数的二进制是长度为 \(len\) 并且循环节长度为 \(k\) 的,记为 \(Ans_{len,k}\) ,这个可以用数位dp快速求,做法和 SP10649 Mirror Number 类似,就是在数位dp的过程中开一个辅助数组来记录当前状态对应的循环节是什么。
求出了 Ans 数组后,发现直接把 Ans 数组里所有的数的和加起来会出现算重的情况,比如长度为 8,循环节长度为 4 的情况中一定包含了长度为 8,循环节长度为 2 的情况,那么把他们减去就行了。也就是
\[Ans_{len,k} \leftarrow Ans_{len,k}-\sum _{g<k,g|k} Ans_{len,g}
\]
注意删的顺序,要从小到大删,也就是保证删 k 的时候 k 的因子已经不存在算重了。
\(\texttt{Code:}\)
#include <bits/stdc++.h>
#define reg register
#define int long long
#define mem(x,y) memset(x,y,sizeof x)
#define ln puts("")
using namespace std;
template <class t> inline void read(t &s){
s=0;reg int f=1;reg char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))s=(s<<3)+(s<<1)+(c^48),c=getchar();s*=f;return;}
template<class t,class ...A> inline void read(t &x,A &...a){read(x);read(a...);return;}
template <class t> inline void write(t x){if(x<0)putchar('-'),x=-x;int buf[21],top=0;
while(x)buf[++top]=x%10,x/=10;if(!top)buf[++top]=0;while(top)putchar(buf[top--]^'0');
return;}
int a[70],t[70],f[70][70],Len,D;// Len:枚举的数在二进制下的长度, D:枚举的循环节长度,t辅助数组
int Ans[70][70];
inline int dfs(int dep,int zero,bool lim)
//dep:当前枚举到的位数,zero表示从哪一位开始的算的,用来弄前导0之类的
{
if(!dep)
return zero==Len; // 长度是否跟枚举的长度一样
if(!lim&&~f[dep][zero])
return f[dep][zero];
reg int maxi=lim?a[dep]:1,res=0;
for(int i=0;i<=maxi;++i)
{
reg int nzero=zero;
if(i&&!nzero)
nzero=dep; // 当前位置开始算,之前都是前导零
if(nzero-dep<D) // 还没有填满 D 个数
{
t[nzero-dep]=i;
res+=dfs(dep-1,nzero,lim&&(i==maxi));
}
else if(t[(nzero-dep)%D]==i) // 看是否能跟循环节一样
res+=dfs(dep-1,nzero,lim&&(i==maxi));
}
if(!lim)
f[dep][zero]=res;
return res;
}
inline int calc(int x,int l,int d)
{
reg int cnt=0;
while(x)
a[++cnt]=(x&1),x>>=1;
mem(f,-1);
Len=l;
D=d;
return dfs(cnt,0,true);
}
signed main(void)
{
reg int l,r;read(l,r);
for(int i=1;i<=64;++i)
for(int j=1;j<i;++j)
if(!(i%j))
Ans[i][j]=calc(r,i,j)-calc(l-1,i,j);
for(int i=1;i<=64;++i) // 去重
for(int j=1;j<i;++j)
if(!(i%j))
for(int k=1;k<j;++k)
if(!(j%k))
Ans[i][j]-=Ans[i][k];
reg int ans=0;
for(int i=1;i<=64;++i)
for(int j=1;j<i;++j)
ans+=Ans[i][j];
write(ans),ln;
return 0;
}

浙公网安备 33010602011771号