Description
ztxz16从小立志成为码农,因此一直对数的二进制表示很感兴趣。今天的数学课上,ztxz16学习了等差数列的相关知识。我们知道,一个等差数列可以用三个数A,B,N表示成如下形式:
B + A, B + 2 * A, B + 3 * A, …, B + N * A
ztxz16想知道对于一个给定的等差数列,把其中每一项用二进制表示后,一共有多少位是1,但他的智商太低无法算出此题,因此寻求你的帮助。
Solution
看到这种数论题就没有淦的欲望(结果这并不是数论QwQ)
发现A较小,考虑把B%A,
剩下直接上数位DP(并不知道为什么m那么小),
设f[i][j]表示做到m的二进制i为,余数为j,这里的余数只用记录二进制大于等于i的部分,也就是只有大于等于
2i
对后面的有影响,
再枚举当前位选什么即可
复杂度: O(Alog(m))
Code
#include <cstdio>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long LL;
const int N=25000,M=55;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n;
LL B,m,ans;
LL er[M+5];
LL f[M+5][N],g[M+5][N];
LL f1[M+5][N],g1[M+5][N];
LL Doit(int n,LL B,LL m)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(f1,0,sizeof(f1));
memset(g1,0,sizeof(g1));
f1[1][B]=g1[1][B]=1;
fo(i,1,M)fo(j,0,n-1)if(f1[i][j])
{
f[i+1][(j+n)>>1]+=f[i][j]+(1&(j+n))*f1[i][j];
f1[i+1][(j+n)>>1]+=f1[i][j];
f[i+1][j>>1]+=f[i][j]+(1&j)*f1[i][j];
f1[i+1][j>>1]+=f1[i][j];
if(m&er[i])
{
g[i+1][j>>1]+=f[i][j]+(1&j)*f1[i][j];
g1[i+1][j>>1]+=f1[i][j];
if(g1[i][j])
{
g[i+1][(j+n)>>1]+=g[i][j]+(1&(j+n))*g1[i][j];
g1[i+1][(j+n)>>1]+=g1[i][j];
}
}else if(g1[i][j])
{
g[i+1][j>>1]+=g[i][j]+(1&j)*g1[i][j];
g1[i+1][j>>1]+=g1[i][j];
}
}
LL ans=0;
fo(i,0,n-1)ans+=g[M][i];
return ans;
}
int main()
{
int q,w,_;
er[1]=1;fo(i,2,M+3)er[i]=er[i-1]<<1;
for(read(_);_;--_)
{
scanf("%d%lld%lld",&n,&B,&m);
m--;B+=(LL)n;
ans=Doit(n,B%n,m+B/n);
printf("%lld\n",ans-Doit(n,B%n,B/n-1));
}
return 0;
}