lowbit

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

题目描述
这天,LYK在学习树状数组。
当它遇到一个叫lowbit的函数时有点懵逼。lowbit(x)的意思是将x分解成二进制,它的值就是2^k,其中k是最小的满足(x & 2^k)>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

读完题面
先看数据范围
n^2的纯暴力可以过30%的数据
常规的暴力分

然而
我们又注意到了

对于另外10%的数据ai<=1
对于再另外10%的数据ai<=3
对于再再另外20%的数据ai<1024

看起来很小啊
试试记录ai的数量
然后再进行暴力
lowbit(a[i] xor a[j])*num[a[i]]*num[a[j]]
这样的感觉

现在
我们能拿到70分了

之后的正解
我自己是想不出来啦……

以下引用

lowbit(x,y)
把x和y都分解成二进制
奇数(二进制末位是1),偶数(二进制末位是0)

lowbit(x^y)=1

把奇数放到左边,假设有x个
把偶数放到右边,假设有y个
x*y*2 对 lowbit起来是等于1

lowbit(x^y)=2
x和y全奇 或者 全偶
假如x和y全奇数
二进制倒数第二位一个是0一个是1
倒数第二位是0 倒数第二位是1

1 2 3 4 5
001 010 011 100 101

001 010
011 100
101

3*2*2 * 1 = 12

011 001         010 100
101

1*2*2 * 2 = 8      1*1*2 * 2=4

101 001
1*1*2 * 4 = 8

分治完之后, 二元组一个在左边,另一个在右边,这样的贡献是很容易统计的
对于每一个数,它只在最多31层中被分治到,总时间复杂度是n*31

正解

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#include<climits>
#include<string>
#include<cstdlib>
#include<ctime>
#define MOD 1000000007
#define MAXN 999999999
#define LL long long
using namespace std;

LL a[100005],b[100005],ans;
LL n;

void work(LL m,LL t)
{
    LL l,r,p,i;

    if(m<=1||t>=30) return;

    l=0;
    for(i=1;i<=m;i++)
        if(a[i]&(1<<t))
        {
            l++;
            b[l]=a[i];
        }

    r=l;
    for(i=1;i<=m;i++)
        if(!(a[i]&(1<<t)))
        {
            r++;
            b[r]=a[i];
        }

    for(i=1;i<=m;i++) a[i]=b[i];
    ans=(ans+(l*(m-l)%MOD)%MOD*(1<<t)%MOD)%MOD;
    work(l,t+1);

    p=0;

    for(i=l+1;i<=r;i++)
    {
        p++;
        a[p]=a[i];
    }

    work(p,t+1);
}

int main()
{
    freopen("lowbit.in","r",stdin);
    freopen("lowbit.out","w",stdout);   

    int i;

    scanf("%lld",&n);

    for(i=1;i<=n;i++)
        scanf("%lld",&a[i]);

    work(n,0);

    ans=(ans*2)%MOD;

    printf("%lld",ans);
    return 0;
}

 
 
 
70

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#include<climits>
#include<string>
#include<cstdlib>
#include<ctime>
#define MOD 1000000007
#define MAXN 999999999
#define LL long long
using namespace std;

LL n,x,num[100005],f[100005],k[100005],p,i,j,tmp,x1,x2,ans,maxn,nnn[100005];

LL lowbit(LL emt)
{
    return emt&(-emt);
}

int main()
{
    freopen("lowbit.in","r",stdin);
    freopen("lowbit.out","w",stdout);   

    scanf("%lld",&n);

    for(i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        nnn[i]=x;
        maxn=max(maxn,x);

        if(x<=1024)
        {
            num[x]++;

            if(!f[x])
            {
                f[x]=1;
                p++;            
                k[p]=x;

            }   
        }
    }

    if(maxn<=1024)
    {
        for(i=1;i<p;i++)
            for(j=i+1;j<=p;j++)
            {
                x1=k[i];
                x2=k[j];

                tmp=2*lowbit(x1^x2)%MOD*(num[x1]%MOD*num[x2]%MOD)%MOD;
                ans=(ans+tmp)%MOD;
            }       
    }

    else
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                tmp=lowbit(nnn[i]^nnn[j])%MOD;
                ans=(ans+tmp)%MOD;
            }

    }

    printf("%lld",ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值