[Codeforces_gym_103414] G.Maximaze XOR sum

传送门

Description

题意:两个数组 \(A\)\(B\),可以执行任意次交换 \(A[i]\)\(B[i]\),是的两个数组的异或和的和最大。

\(A[i],B[i]\le10^{18}\)\(n\le 10^5\)


Solution

一开始两个数组的异或和分别为 \(X_A\)\(X_b\),交换对应下标的数等价于让 \(X_A\)\(X_b\) 同时异或上 \(C[i] = A[i]\oplus B[i]\)

题目等价于有两个数 \(X_A\)\(X_b\) ,和一个数组,从数组中选取任意个数,让它们的异或和异或上 \(X_A\)\(X_b\) 后的和最大。

显然可以按照每一位来贪心选取这一位是 \(1\) 还是 \(0\)

如果这一位取 \(1\)\(0\) 效果是一样的,考虑直接把所有数的这一位都置为 \(0\),在后续不做考虑。

可以求出 \(C\) 数组的线性基,在求解的过程中,对于每一个 \(base_i\) 求出它是由哪些个数异或在一起的。

这一部分的复杂度是 \(O(n\log^210^{18})\)

之后考虑每一位,如果这一位需要是 \(1\) 并且对应的 \(base_i\) 存在,那么就取用这个基。


Code

#include<bits/stdc++.h>
#define ll long long
#define db double
#define LL __int128
#define DB __float128
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"\n"
#define dbg3(x) cerr<<#x<<"\n"
using namespace std;
#define reg register
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug debug("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;++i)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;--i)
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
inline long long read() {
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MN = 1e5 + 5;
const int log_MN = 70;
long long a[MN], base[log_MN], n, c[MN];
std::bitset<MN> d[log_MN], da; 
inline void calc()
{
    int N = 63;
    register int i, j, k;
    for(i = 1;i <= n; ++i) {
        da.reset();da[i] = 1;
        ll tmp = a[i];
        int flg = 0;
        for(j = N; ~j; --j)
            if(tmp>>j&1) {
                if(base[j]) tmp ^= base[j];
                else {
                    flg = 1;
                    break;
                }
            }
        if(!flg) continue;
        for(j = N; ~j; --j)
        if(a[i]>>j&1) {
            if(base[j]) a[i] ^= base[j], da ^= d[j];
            else {
                base[j] = a[i];
                d[j] = da;
                break;
            }
        }
    }
}
long long A = 0, B = 0;

bool h[MN];
long long get(long long x) {
    long long ret = 0;
    for(int i = 63; ~i; --i) {
        int bi = (x>>i)&1;
        if(!h[i]) ret = ret<<1|bi;
        else ret = ret<<1;
    }
    return ret;
}

int main() {
    n = read();
    A = 0, B = 0;
    for(int i = 1; i <= n; ++i) {
        long long x = read();
        c[i] ^= x;
        A ^= x;
    }
    for(int i = 1; i <= n; ++i) {
        long long x = read();
        c[i] ^= x;
        B ^= x;
    }
    memset(h, 0, sizeof h);
    for(int i = 63; ~i; --i) {
        int _a = A>>i&1, _b = B>>i&1;
        if(_a != _b) {
            h[i] = 1;
        }
    }
    for(int i = 1; i <= n; ++i) {
        a[i] = get(c[i]);
    }
    // ll tmp = A, pos = B;
    A = get(A); B = get(B);
    calc();
    da.reset();
    for(int i = 63; ~i; --i) {
        int _a = A>>i&1, _b = B>>i&1;
        if(_a == 0 && _b == 0) {
            // 1
            if(base[i]) {
                A ^= base[i];
                B ^= base[i];
                da ^= d[i];
            }
        }
        else if(_a == 1 && _b == 1) {
            // 0
            // nothing happened
        }
    }
    long long _ = 0;
    for(int i = 63; ~i; --i) {
        _ = (_<<1)|h[i];
    }

    printf("%lld %d\n", A + B + _, da.count());
    for(int i = 1; i <= n; ++i) {
        if(da[i] == 1) printf("%d ", i);
    }
    return 0;
}

posted @ 2022-08-08 17:08  PaperCloud  阅读(61)  评论(0编辑  收藏  举报