题意:
题意是个啥?我怎么读不懂?
这道题没过的都是因为没读懂题吧!!!!!!简直考阅读理解啊!!!
赛后参考(茶飘香~)大佬的博客终于读懂了题意:
题意:有一个集合,你每向集合中加入一个数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;
}