1. 程式人生 > >2016 大連區域賽 現場賽 E—Aninteresting game【樹狀陣列】

2016 大連區域賽 現場賽 E—Aninteresting game【樹狀陣列】

Aninteresting game

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

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

Hint

lowbit(i) =i&(-i).It means the size of the lowest nonzero bits in binary of i. For example, 610=1102, lowbit(6) =102= 210 When we add 8,we should bring [1,7] and 8 into new set. When we add 9,we should bring [9,8] (empty) and 9 into new set. So the first answer is 8+1=9. When we add 6 and 8,we should put 6 into new sets. So the second answer is 2.

題意:

每次查詢有兩種操作

op1:求加入L~R的數時所消耗的單元

op2:求將x加入集合或移動到其它集合所消耗的單元(即由x引起消耗的單元)

思路:op1:每次加入一個數i 那麼同時會移動[i-lowbit(i)+1 , i-1] ,總的消耗是i-(i-lowbit(i)+1) +1=lowbit(i) 所以每次加入一個數對應的消耗是2的冪次,1..n的消耗是n包含的2的冪次與個數的乘積ans+=(n/(1<<i)-n/(1<<(i+1)))*(1<<i),那麼求L~R求差就行。

解釋一下,n/(1<<i)-n/(1<<(i+1))表示長為2^i的消耗的數的個數,例如:n=10 , 包含長為2的數是2,6,10 為什麼4,8不是,因為它們雖然是2的倍數,但更是4的倍數,包含更長的區間了,所以這部分要減去。

op2:由樹狀陣列可知 [i-lowbit(i)+1 , i-1] 是以i為根節點對應的區間,如果假如的數能夠移動i ,那麼這個數對應的孩子區間一定包含i ,所以從x向上一直找父節點即可。

程式碼:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
const int maxn=100010;
const ll mo=1e9+7;
ll n,m,k,q;
ll ans,tmp,cnt;
ll lb(ll x){return x&(-x);}
ll query(ll x)
{
    ll ans=0;
    while(x<=n)
    {
        ans++;
        x+=lb(x);
    }
    return ans;
}
ll cal(ll x)
{
    ll tmp=1,ans=0;
    while(tmp<=x)
    {
        ans+=((x/tmp)-(x/(tmp<<1)))*tmp;
        tmp<<=1;
    }
    return ans;
}
int main()
{
    while(scanf("%lld%lld",&n,&q)!=EOF)
    {
        int op;
        ll x,y;
        while(q--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%lld%lld",&x,&y);
                printf("%lld\n",cal(y)-cal(x-1));
            }
            else
            {
                scanf("%lld",&x);
                printf("%lld\n",query(x));
            }
        }
    }
    return 0;
}