2016年ACM/ICPC大连赛区 E题(阅读理解+树状数组原理)

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5749

题意:

题意是个啥?我怎么读不懂?

这道题没过的都是因为没读懂题吧!!!!!!简直考阅读理解啊!!!

赛后参考(茶飘香~)大佬的博客终于读懂了题意:

题意:有一个集合,你每向集合中加入一个数i,则就要把[i-lowbit(i)+1 , i-1] 的数都加进去,加入一个数消耗为1。

q(q<=1e5)次查询

每次查询有两种操作

op1:求加入L~R的数时的消耗

op2:求将x加入集合或移动到其它集合的消耗(即由x引起的消耗的单元的数量)

思路:

op1:每次加入一个数i 那么[i-lowbit(i)+1 , i-1]的数就都要加进去,总的消耗是i-(i-lowbit(i)+1) +1=lowbit(i)

所以每次加入一个数对应的消耗是2的幂次。因此可以O(logn)求出加入1~n这n个数时的消耗。

那么求L~R即可以枚举幂次,即: ans+=(n/(1<<i)-n/(1<<(i+1)))*(1<<i)

解释一下,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;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值