lowbit

来源:互联网 发布:软件调试什么意思 编辑:程序博客网 时间:2024/05/23 20:38

树状数组(lowbit)
Time Limit:1000ms Memory Limit:128MB

题目描述
这天,LYK在学习树状数组。
当它遇到一个叫lowbit的函数时有点懵逼。lowbit(x)的意思是将x分解成二进制,它的值就是,其中k是最小的满足(x & )>0的数。(&是二进制中的and运算)
LYK甚至知道lowbit(x)=(x&-x)。但这并没什么用处。
现在LYK有了n个数字,为了使自己更好的理解lowbit是什么意思。它想对所有n^2个二元组求lowbit。具体的,对于一个二元组(ai,aj),它的值为lowbit(ai xor aj) (xor表示异或的意思),那么总共有n^2对二元组,LYK想知道所有二元组的值加起来是多少。
这个答案可能很大,你只需输出这个值对1000000007取模后的结果就可以了。

输入格式(lowbit.in)
第一行一个数n,表示有n个这样的数字。
第二行n个数ai。

输出格式(lowbit.out)
一个数表示答案。

输入样例
5
1 2 3 4 5

输出样例
32

数据范围
对于30%的数据n<=1000。
对于另外10%的数据ai<=1。
对于再另外10%的数据ai<=3。
对于再再另外20%的数据ai<1024。
对于100%的数据1<=n<=100000,0<=ai<2^30。

第一档:观察一下数据范围,前30%,n<=1000,可以用n^2的方法拿到;
第二档:再观察一下数据,30%~70%,ai<1024,那此处就涌现了一种非常机智的方法,
记录下每个数的个数,那就可以用O(m^2)方法可以做了(其中m是ai中最大的数)。
第三档:就是正解了,好像有两种方法,一是tire树(蒟蒻我不会啦),另一种是分治。
先来分析一下题的本质,lowbit(x)是x二进制数第一个1的权值,如lowbit(3)=1;
lowbit(6)=2;(6的二进制为110);
两个数异或:异或是对两个二进制数每一位异或,如果一样就为1,不一样就为0,例:
3^2=1:二进制数11 与 10 按位异或为01
* 那两个数异或后取lowbit就是两个数从后往前第一个不一样的的位置的对应的1的权值。*
那两个数对答案的贡献就是这两个数从后往前数第一个不一样的位置放上1 得到的数的大小
(*2,因为a和b,b和a对答案都有贡献)。
那么我们就可以从第一位开始,一位一位的处理(之所以可以从后面开始一位一位的处理,
是因为上面打星号的那句话 :-),前面的不一样的情况对答案就没有什么贡献了),
当前这一位不一样的,可以分为两组(因为当前这一位一样的话,这一位对答案就没有贡献了),
所以,我们可以根据当前这一位的异同,往下分治。(当前这一位的异同可以根据 &(1<< t)来
判断,那分成的两组数中,从一组中选出一个数x,从另一组中选出一个数y,对答案的贡献也
就是(1<< t),那这两组数结合对答案的贡献就是m1*m2*(1<< t)*2)。
可以得到启示,当我们想不出正解时,在考场上写好暴力也可以得到较高的分。

第二档的代码:

#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>#define LL long long#define MOD 1000000007using namespace std;int n,a[100009],num[100009],maxn;LL ans;int lowbit(int x){return x & -x;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]),maxn=max(maxn,a[i]);    if(maxn<1024)    {        for(int i=1;i<=n;i++) num[a[i]]++;        for(int i=0;i<=maxn;i++)          for(int j=i+1;j<=maxn;j++)            ans=(ans+1ll*lowbit(i^j)*num[i]*num[j])%MOD;        printf("%lld",ans*2%MOD);return 0;    }       else     {        for(int i=1;i<=n;i++)         for(int j=1;j<=n;j++)          ans=(ans+lowbit(a[i]^a[j]))%MOD;        printf("%lld",ans);return 0;    }}

第三档的代码:

#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#define MOD 1000000007using namespace std;int n,a[100009],b[100009];long long ans;void work(int m,int t){    if(m<=1||t>=30) return;    int L=0;    for(int i=1;i<=m;i++) if(a[i]&(1<<t)) b[++L]=a[i];    int R=L;    for(int i=1;i<=m;i++) if(!(a[i]&(1<<t))) b[++R]=a[i];    ans=(ans+1ll*L*(m-L)*(1<<t)*2)%MOD;    for(int i=1;i<=L;i++) a[i]=b[i];    work(L,t+1);//分治     int C=0; for(int i=L+1;i<=m;i++) a[++C]=b[i];    work(C,t+1);}int main(){    freopen("lowbit.in","r",stdin);    freopen("lowbit.out","w",stdout);    scanf("%d",&n);     for(int i=1;i<=n;i++)     scanf("%d",&a[i]);    work(n,0);    printf("%lld",ans%MOD);}