【打表枚举+二分】喜爱

喜爱

题目描述:

小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,R106T10
对于60%的数据: L , R ≤ 1 0 10 , T ≤ 100 L,R≤10^{10},T≤100 L,R1010T100
对于100%的数据: L , R ≤ 1 0 18 , T ≤ 10000 L,R≤10^{18},T≤10000 L,R1018T10000

解题思路:找出二进制表示的情况下,所有位数都为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 2631,我打表的情况是 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
*/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值