@TopCoder - 12197@ XorAndSum


@description@

给出 N 个数,每次操作可以任意选择两个数,将其中一个替换为两个数的异或。

求任意次操作后,最终所有数的和的最大值。

Class:
XorAndSum
Method:
maxSum
Parameters:
long[]
Returns:
long

sample:
{1,2,3}
Returns: 8

constraints
数的数量 N <= 50,保证所有数的权值 <= 10^15。

@solution@

最大、异或这些关键字,不难想到线性基。
同时可以发现,题目中给出的操作颇有些像高斯消元中,将一个方程异或到另一个方程的操作。
这更坚定了我们写线性基的决心。

我们可以类比高斯消元的做法,将线性基外的数全部消成 0,然后再通过线性基异或成可以异或得到的最大值。
考虑线性基中的数怎么才能取到最大值。

我们将线性基继续类比高斯消元进行简化,将每一行的主元(如果有)所在列全部异或成 0。
然后?可以发现一个大小为 k 的线性基可以异或出 2^k 种数,而我们一共有 k 个主元,决定每一个主元是选还不是选的方案数也是 2^k。进而两者是一一对应的。
我们希望最终线性基中的每一个数都是最大值:即选择所有的主元的那种方案。但是我们可以发现,根据线性基的定义可以简单反证假如有两个以上的主元在自己所在列的每一行中都为 1,会导致矛盾。
这意味着对于主元所在的列,必然存在一个 0,只有一列会例外。显然我们将这个例外放在最大的主元是最优的。
于是线性基中最大和就可以构造出来了:有一个数是选择所有主元(即能异或出的最大值),其他数总是缺一个主元。

我也不知道我上面写了啥。大家也可以看看yhn学长的博客

@accepted code@

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
class XorAndSum{
    public:
    ll b[60 + 5];
    void insert(ll x) {
        for(int i=60;i>=0;i--)
            if( x & (1LL<<i) ) {
                if( b[i] == 0 )
                    b[i] = x;
                x ^= b[i];
            }
    }
    ll get_max() {
        for(int i=60;i>=0;i--)
            for(int j=i-1;j>=0;j--)
                if( b[i] & (1LL<<j) )
                    b[i] ^= b[j];
        ll ret = 0;
        for(int i=60;i>=0;i--)
            ret ^= b[i];
        return ret;
    }
    ll maxSum(vector<ll>v) {
        for(int i=0;i<v.size();i++)
            insert(v[i]);
        ll x = get_max(), ans = v.size()*x;
        bool fir = true;
        for(int i=60;i>=0;i--)
            if( b[i] ) {
                if( fir )
                    fir = false;
                else
                    ans = ans - x + (x^b[i]);
            }
        return ans;
    }
};

@details@

老师:这个不是线性基的入门题?你考试的时候连这个都写不起?

我:……

怎么办整个世界都在嘲讽我。。。

转载于:https://www.cnblogs.com/Tiw-Air-OAO/p/11128702.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值