CF 2146D1 Max Sum OR(Easy)

Link

题目:给定元素为0, 1, ……, r - 1, r所有整数的两个相同数组,要求重新排序其中一个数组,使得排序后的∑(ai | bi)最大, 给出最大值和构造。

思路:
首先,题目这样问,我们先考虑能取到最大值是多少,以及大致什么时候可能会更大一些。
异或的性质:x | y = (x + y) - (x & y) <= x + y
因此,是否能给出一个构造,满足 ∑(ai + bi) = 2 * ∑(ai) = (r + 1) * r呢?
(或者观察题目给的样例的output, 也能发现规律)
也就是说,我们要重排数列an,满足对任意的0<=i<=r,都有 ai & i = 0;
写出几个小样例的二进制,考虑一下,一目了然了:
显然只需尽可能地对于任意两个数的配对,要没有重叠的比特位。而大数要满足的要求更多,且往往和小数配对,因此,我们从r开始暴力,直到0.
不难注意到,对于一个数x,和它满足要求的配对是2 ** k - 1 - x, 其中 2 ** k > x, 也就是说,k只有最小值的要求。
那么我们根据r求出k的最小值,然后依次遍历,如果发现遍历到一个数x,它的2 ** k - x - 1已经被比它更大的数占用了,那么就k--;
easy version的简单之处就体现出来了:l = 0 =>总能找到这样的配对。

code

#include<bits/stdc++.h>
using namespace std;
#define ll long long


void solve() {
    int l, r; cin>>l>>r;
    int s = 1;
    int pp = 1;
    while (pp <= r) {
        s++;
        pp = pp << 1 | 1;
    }
    set<int> st;
    for (int i = 0; i <= r; ++i) st.insert(i);
    std::vector<int> ans(r + 1);
    for (int i = r; i>= 0; --i) {
    
        while (st.find(pp - i) == st.end()) {
            s--;
            pp >>= 1;
        }
        ans[i] = pp - i;
        st.erase(ans[i]);
    }

    cout<<1LL * (r + 1) * r<<endl;
    for (int i = 0; i <= r; ++i) cout<<ans[i]<<" ";
    cout<<endl;
}

int main() {
    int tt; cin>>tt;
    while (tt--) solve();
    return 0;
}
posted @ 2025-12-09 21:28  Wuyou2008  阅读(5)  评论(0)    收藏  举报