Number
题目描述
如果一个数能够表示成两两不同的 3 的幂次的和,就说这个数是好的。
比如 13 是好的,因为 13 = 9 + 3 + 1 。
又比如 90 是好的,因为 90 = 81 + 9 。
现在我们用 a[i] 表示第 i 小的好数。
比如 a[1] = 1, a[2] = 3, a[5] = 10 。
给定 L,R,请求出 a[L] 到 a[R] 的 和 mod 232。
输入格式
第一行一个整数 T,表示数据组数。
接下来 T 行,每行两个整数 L,R 表示一组询问。
输出格式
输出 T 行,每行为一个整数,表示相应询问的答案。
样例数据 1
输入
5
1 3
3 3
4 5
6 7
2 5
输出
8
4
19
25
26
备注
【数据范围】
对 30% 的输入数据:1≤T≤100;R≤1000 。
对 100% 的输入数据:1≤T≤100000;1≤L≤R≤1018 。
解析:
首先我们可以发现将 a[i] 转化成二进制再转成三进制就是第 i 小的数,于是我们可以用类似数位DP的的方法完成,详情见代码。
代码:
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int Max=61;
const int mod=4294967296;
int l,r,t;
int Pow[Max],sum[Max];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-'){f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void pre()
{
Pow[0]=sum[0]=1;
for(int i=1;i<=60;i++) Pow[i]=Pow[i-1]*3,sum[i]=sum[i-1]+Pow[i];
}
inline int solve(int num)
{
int ans=0,now=0;
for(int i=60;i>=1;i--)
if((1LL<<i)&num)
{
ans+=sum[i-1] * (1LL<<(i-1)); //计算每一位数出现的次数之和乘以前缀和
ans+=now * (1LL<<i);
ans+=Pow[i],now+=Pow[i];
}
if(num&1) ans+=now+1; //最后一位需要特判
return ans;
}
signed main()
{
pre();
t=get_int();
while(t--)
{
l=get_int(),r=get_int();
cout<<(solve(r)-solve(l-1))%mod<<"\n";
}
return 0;
}