喜爱
题目描述:
小s最近对数字情有独钟。他又发现了一种神奇的数字。对于数x,如果它二进制表示中只有一位是0,则x就会被小s所喜爱。比如5,二进制为101,则它被小s所喜爱。
现在,小s想知道,对于一个区间[L,R],有多少数是他所喜爱的。
输入:
输入包含多组数据。
输入第一行T,表示数据组数。
每组数组仅有一行,包含两个正整数[L,R]。
输出:
对于每组数据输出一行,表示答案。
样例输入 :
2
5 10
2015 2015
样例输出 :
2
1
提示:
对于30%的数据: L , R ≤ 1 0 6 , T ≤ 10 L,R≤10^6,T≤10 L,R≤106,T≤10
对于60%的数据: L , R ≤ 1 0 10 , T ≤ 100 L,R≤10^{10},T≤100 L,R≤1010,T≤100
对于100%的数据: L , R ≤ 1 0 18 , T ≤ 10000 L,R≤10^{18},T≤10000 L,R≤1018,T≤10000
解题思路:找出二进制表示的情况下,所有位数都为1的情况,即就是2的幂减1就是了。然后拿这个数分别减指数比他的指数至少低2的所有情况,然后一一列举出来就是了,表建好之后,再来个for循环就ok
例如:
对于这道题的话,我写了两个代码,不过有一个是辅助性代码,就是为了检验我打的表是否正确而建的,在这里我把两个代码都附上吧;
代码一(检验建表数据是否正确的暴力代码):
#include<bits/stdc++.h>
using namespace std;
string trans(int x)
{
string a;
while(x)
{
a+=(x%2)+48;
x/=2;
}
reverse(a.begin(),a.end());
return a;
}
bool judge(string a)
{
int count1=0;
for(int i=0;i<a.size();i++)
{
if(a[i]=='0')
count1++;
}
if(count1==1) return true;
else return false;
}
int main()
{
for(int i=1;i<=100;i++)
{
cout<<trans(i)<<" ";
if(judge(trans(i))) cout<<i;
cout<<endl;
}
return 0;
}
*运行截图也带上 *
代码二(AC代码):
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
ll st[1001011];
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1){
ans=ans*a;
b--;
}
a=a*a;
b/=2;
}
return ans;
}
int main()
{
ll k=0,i,j;
for(i=1;i<=60;i++)
{
ll temp=qpow(2,i)-1;
for(j=0;j<i-1;j++)
st[k++]=temp-qpow(2,j);
}
sort(st,st+k);
// cout<<st[k-1];
// cout<<k<<endl; k=1770
// for(i=0;i<k;i++) //检验数据
// {
// cout<<st[i]<<endl;
// }
ll t,L,R;
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&L,&R);
i=0;
ll count1=0;
while(st[i]<L) i++;
for(;i<k;i++)
{
if(st[i]>R) break;
count1++;
}
printf("%lld\n",count1);
}
return 0;
}
代码里我用了无符号型的long long 因为我看到数据范围是1e18,我怕爆数据,所以就整上了unsigned,但是后来同学告诉我说直接long long 也能过,一看我的代码,诶呀,虽说定义了无符号类型,但还是用的%lld,自我觉得也是用的普通的long long 的范围,网上搜一下也会发现,long long的最大值确实会比
1
0
18
10^{18}
1018大一些,用幂的形式来表示的话是
2
63
−
1
2 ^{63}-1
263−1,我打表的情况是
2
60
2 ^{60}
260 所以,也不用担心爆了。
时间复杂度的话,感觉我这个挺牵强的,正好卡过,T的范围1e4,打表的数据一共有1770个,所以就是O(1e4*1770) 差不多2e7了,卡过也实属运气呀!哈哈哈哈。。。
不对不对,又看了一眼评测结果,50ms,,挺快呀,说明什么,代码复杂度还是挺低的嘛,可能是我复杂度算错了,交的时候都是小心翼翼的,就怕被T,可吓死我了,,大佬路过的的话帮忙指点一下复杂度是多少呀,我不会算qwq,非常感谢!!!
2021.03.13 updata.
对于上述代码中可以加上二分进行进一步的优化。
二分优化代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll st[1800], s;
ll qpow(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1) res = 1ll * res * a;
a = 1ll * a * a;
b >>= 1;
}
return res;
}
int main()
{
//freopen("7.in","r",stdin);
//freopen("7.out","w",stdout);
for (int i = 1;i <= 60;i++)
{
ll temp = qpow(2, i) - 1;
for (int j = 0;j < i - 1;j++)
st[++s] = temp - qpow(2, j);
}
sort(st + 1, st + s + 1);
//for(int i=1;i<=s;i++) cout << st[i] << endl;
int t;
ll L, R;
scanf("%d", &t);
while (t--)
{
scanf("%lld%lld", &L, &R);
int l = lower_bound(st + 1, st + s + 1, L) - st;
int r = upper_bound(st + 1, st + s + 1, R) - st - 1;
printf("%d\n", r - l + 1);
}
return 0;
}
/*
1
10
11
100
101
110
111
1000
1001
1010
1011
1100
1101
1110
1111
*/