题意:给你1到n,这个n个数从小到大排列,将这n个数放在相应集合中
构成n个集合,每个集合中的数为[i-lowbit(i)+1,i-1]+i
有两种查询:(1)集合[a,b]共有多少个数 (2)数字x在几个集合里面
题解:一开始是懵逼的,n有1e18这么大,但是有lowbit不免会让我想起树状数组。。。
因为对树状数组的原理忘得几乎差不多了,于是又回忆了一发。
我们可以将小数列出来看一看有什么启示没有
1 2 3 4 5 6 7 8
1 2 1 4 1 2 1 8
我们可以发现,每k个 k ∈{1,2,4,8……}会出现1个k的倍数,所以我们能够知道[1,i]中有多少个不同的k以及每个k的个数p(类似于容斥原理)。所以能够求得[1,i]的长度和是多少,进而求得[a,b]的长度和。要找到x在几个集合中,可用x=x+lowbit(x)找出有多少满足条件的x,就能得到集合的个数。
#include<stdio.h>
typedef long long ll;
ll tmp[65],n,m;
ll findans(ll x)
{
ll ans=0;
for(ll i=0;tmp[i]<=x;i++)
ans+=(x/tmp[i]-x/tmp[i+1])*tmp[i];
return ans;
}
int main(void)
{
int i;
tmp[0]=1;
for(i=1;i<=63;i++)
tmp[i]=tmp[i-1]*2;
while(scanf("%lld%lld",&n,&m)!=EOF)
{
ll x,y,t;
while(m--)
{
scanf("%lld",&t);
if(t==1)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",findans(y)-findans(x-1));
}
else
{
ll ans=0;
scanf("%lld",&x);
while(x<=n)
{
ans++;
x+=x&(-x);
}
printf("%lld\n",ans);
}
}
}
return 0;
}