HDU 5975 Aninteresting game(树状数组原理题)

Aninteresting game

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 210    Accepted Submission(s): 83


Problem Description
Let’s play a game.We add numbers 1,2...n in increasing order from 1 and put them into some sets.
When we add i,we must create a new set, and put iinto it.And meanwhile we have to bring [i-lowbit(i)+1,i-1] from their original sets, and put them into the new set,too.When we put one integer into a set,it costs us one unit physical strength. But bringing integer from old set does not cost any physical strength.
After we add 1,2...n,we have q queries now.There are two different kinds of query:
1 L R:query the cost of strength after we add all of [L,R](1≤L≤R≤n)
2 x:query the units of strength we cost for putting x(1≤x≤n) into some sets.
 

Input
There are several cases,process till end of the input.
For each case,the first line contains two integers n and q.Then q lines follow.Each line contains one query.The form of query has been shown above.
n≤10^18,q≤10^5
 

Output
For each query, please output one line containing your answer for this query
 

Sample Input
  
  
10 2 1 8 9 2 6
 

Sample Output
  
  
9 2


题目大意:Query1、每次加入一个数新的i,是把[i-lowbit(i)+1,i]的数字都加入集合。每把一个数加入到一个集合内,都要花费一单位能量。统计花费的能量数

最开始用了最原始的方法for循环,统计 [i-lowbit(i)+1,i-1]之间的值,代码是这样的:

int f(int x)

{

    return x-(x-(x&-x));

}

int main()

{

       int sum=0;

    for(int i=1;i<=9;i++)    //例如1~9

    {

           printf("%d",f(i));

           sum+=f(i);

       }

     printf("%d\n",sum);;

    return 0;

}

1 2 1 4 1 2 1 8 1

sum=21  //结果

这样很明显会超时的。。。

观察发现,根据树状数组的原理相当于每个位置放的是1;

奇数都为1,然后偶数当中根据所在树状数组中的位置,依次2,4,8增大,我们只需统计转换为二进制后第0位直到第n位1出现的个数在乘以对应的2,4,8等等2^n即可;

0 0 0 1

0 0 1 0

0 0 1 1

*

*

*

等等

long long getans(long longx)

{

   long long res=0;long long t=1; long long tt;

    for(int i=0;(t<<i)<=x;i++)

    {

        tt=x/(t<<i);

        tt-=x/(t<<(i+1));

        tt*=(t<<i);

        res+=tt;

    }

    printf("res==%lld\n",res);

    return res;

}

这样循环最大的情况就是二进制的位数,所以不会超时

Query2、询问一个x,查询x在1-n的插入中一共被移动了多少次。

       先尝试自己写几个实例

         Numi表示i的转移次数

假设N为10

Num1: 1 2 4 8 ==共4次

Num2: 2 4 8 ==共3次

Num3: 3 4 8 ==共3次

Num4: 4 8 ==共2次

Num5: 5 6 8 ==共3次

*

*

*

Num8:8== 共1次

 

观察规律可知这就是lowbit的走势,代码如下:

scanf("%lld",&a);

       while(a<=n)

        {

              num++;

           a+=a&-a;

       }

       printf("%d\n",num);

 完整代码如下:


#include<bits/stdc++.h>
using namespace std;
long long n;
int q;
long long getans(long long x)
{
   long long res=0;long long t=1; long long tt;
    for(int i=0;(t<<i)<=x;i++)
    {
        tt=x/(t<<i);
        tt-=x/(t<<(i+1));
        tt*=(t<<i);
        res+=tt;
    }
    return res;
}
int main()
{
    while(~scanf("%lld%d",&n,&q))
    {
        int f;
        long long a,b,ans;
        while(q--)
        {
           scanf("%d",&f);
            if(f==1)
            {
                scanf("%lld%lld",&a,&b);
                ans=getans(b)-getans(a-1);
                printf("%lld\n",ans);
            }
            else{
                int num=0;
                scanf("%lld",&a);
                while(a<=n)
                {
                    num++;
                    a+=a&-a;
                }
                printf("%d\n",num);
            }
        }
    }
    return 0;
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值