Aninteresting game HDU - 5975 (树状数组lowbit深入理解)

Aninteresting game

 题目链接:HDU - 5975 

题意:有1~n个数,将i放入集合中时同时放入了[i-lowbit(i)+1, i-1]区间的数,每向集合中放入一个数就耗费一点体力,所以,向集合中放入i时就是同时放入[i-lowbit(i)+1, i]区间的数,共i-(lowbit(i)+1)+1=lowbit(i)个数,消耗的体力就是lowbit(i);问将区间[l, r]的数全部放入集合中需要耗费的体力,及数x在被放入几次;

解析:先看第一问:将区间[l, r]的数全部放入集合中需要耗费的体力就是l~r的lowbit值之和;由于数据太大,直接求[l, r]的所有lowbit再加起来求和会超时(虽然这是O(n)的算法);那么就想能不能直接求lowbit的前缀和这样不就简单多了,问题是如何求前缀和;在此之前先来看一下lowbit求出来的到底是什么;

lowbit(i)求出来的是i的二进制由右向左第一个1出现的位置构成的十进制数;如:

6(十进制)=110(二进制), lowbit(6(十进制))=10(二进制)=2(十进制);

那么前n个数的lowbit前缀和就相当于求前n个数中:1第一次出现在第0位的次数*2^0=1   +    1第一次出现在第1位的次数*2^1=2    +    1第一次出现在第2位的次数*2^2=4    +    1第一次出现在第3位的次数*2^3=8    +    ······

前n个数的二进制中1第一次出现在第i位的次数怎么算?

二进制中如果1第一次出现在第i位,那么该数一定是2^i的倍数且不是2^(i-1)的倍数;

那么前n个数中2^i的倍数的个数减去2^(i+1)的倍数的个数,就是1第一次出现在第i位的次数,那么我们就可以O(logn)的算出前n个数的lowbit和;

再看第二问:x被放入集合中几次?只要x在[i-lowbit(i)+1, i]区间就会被放入一次;

在树状数组中以r为父节点的区间范围是多少?[r-lowbit[i]+1, r];第二问就简化成了x的父节点有几个,就是x到根节点经过的点的个数,如何找根节点?和树状数组的更新是相同的,就是一直r+=lowbit(r),直到r>=n;运算了几次就有几个x

#include <bits/stdc++.h>
using namespace std;
long long lowbit(long long x){
	return x&(-x);
}
long long cal(long long n){
	long long ans=0;
	for(long long p=1; p<=n; p<<=1){
		ans+=(n/p-n/(p<<1))*p;
	}
	return ans;
}
int sum(long long x, long long n){
	int ans=0;
	while(x<=n){
		x+=lowbit(x);
		ans++;
	}
	return ans;
}
int main(){
	long long n, q;
	while(~scanf("%lld%lld", &n, &q)){
		int op;
		while(q--){
			scanf("%d", &op);
			if(op==1){
				long long l, r;
				scanf("%lld%lld", &l ,&r);
				printf("%lld\n", cal(r)-cal(l-1));
			}
			else{
				long long x;
				scanf("%lld", &x);
				printf("%d\n", sum(x, n));
			}
		}
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值