LGP9696 [GDCPC 2023] Swapping Operation 学习笔记

LGP9696 [GDCPC 2023] Swapping Operation 学习笔记

Luogu Link

题意简述

给定长度为 \(n\) 的非负整数序列 \(A\)

定义 \(F(A)=\max_{1\le k< n}((a_1\&a_2\&\dots\&a_k)+(a_{k+1}\&a_{k+2}\&\dots\&a_n))\)。其中 \(\&\) 表示按位与操作。

可以进行至多一次交换操作:交换 \(a_i\)\(a_j\) 的值。问 \(F(A)\) 最大值。

\(n\le 10^5\)\(a_i\in[0,2^{32})\)

做法解析

首先一个显然的性质是:如果要交换 \(a_i,a_j\),那必然有 \(k\in [i,j)\)。否则换了等于没换。

看到前缀后缀与,立马反应关键点只有 \(\log V\) 个。然后你发现交换两个非关键点一定不优。

生动理解:因为关键点仍然杵在那,前缀、后缀最晚一定在它们那变小。你换个别的东西过来可能还会让它们的值提早变小。

交换两个关键点暴力做的时间复杂度是 \(O(n\log^2 V)\),显然可以接受,现在思考交换一个关键点和一个非关键点的情景。

不妨设那个交换的关键点是一个前缀关键点(后缀的同理),下标为 \(p\),另外设那个非关键点下标为 \(j\)。当然我们知道此时 \(p\le k<j\) 才有讨论的意义,因此在我们现在考虑的范围内,最终选的前缀肯定不含 \(a_p\),因此我们这次处理前缀数组时候时忽略掉它。至于现在的后半段?别忘了 \(a_j\) 是非关键的,所以把 \(a_j\) 拿走不会改变 \(suf\),不过 \(suf\) 数组得与上一个 \(a_p\)

但此时直接做这部分是 \(O(n^2\log V)\) 的,我们得优化一下。实际上我们发现讨论所有 \(k\) 取在前缀关键点前面一格的情况就可以覆盖到所有可能的答案了(之前不用这么做是因为复杂度瓶颈不在这)。

稍微解释一句原理。“取 \(k\) 等于前缀关键点 \(x\) 前面一格,就相当于是在前缀数组取 \(pre_{x-1}=pre_w\)\(w\)\(x\) 前面的关键点下标)的情况下让前缀吞尽量多的元素,让后缀少包含一些可能存在的后缀关键点。
其实你讨论所有 \(k\) 取在后缀关键点处的情况也是可以的,这两者等价。

代码实现

#include <bits/stdc++.h>
using namespace std;
const int MaxN=1e5+5,alf=0x7fffffff;
int N,A[MaxN],pre[MaxN],suf[MaxN],ans;
vector<int> pvec,svec;
void acount(){
    pre[0]=suf[N+1]=alf;
    for(int i=1,j=N;i<=N;i++,j--){
        pre[i]=pre[i-1]&A[i];
        suf[j]=suf[j+1]&A[j];
    }
    for(int i=1;i<N;i++)maxxer(ans,pre[i]+suf[i+1]);
}
int solve(int x){
    pre[0]=alf,suf[N+1]=A[x];
    for(int i=1,j=N;i<=N;i++,j--){
        pre[i]=pre[i-1]&(i==x?alf:A[i]);
        suf[j]=suf[j+1]&A[j];
    }
    int res=0;
    for(int l=x+1,r;l<=N;l=r+1){
        for(r=l;r<N&&suf[r+1]==suf[l];r++);
        for(int i=l;i<=N;i++)maxxer(res,(pre[l-1]&A[i])+suf[l]);
    }
    return res;
}
void mian(){
    readi(N);ans=0;
    for(int i=1;i<=N;i++)readi(A[i]);
    acount();pvec.clear(),svec.clear();
    for(int i=1;i<=N;i++)if(pre[i]!=pre[i-1])pvec.push_back(i);
    for(int i=N;i>=1;i--)if(suf[i]!=suf[i+1])svec.push_back(i);
    for(auto p : pvec)for(auto s : svec)if(p!=s)swap(A[p],A[s]),acount(),swap(A[p],A[s]);
    for(auto i : pvec)maxxer(ans,solve(i));
    reverse(A+1,A+N+1);
    for(auto i : svec)maxxer(ans,solve(N+1-i));
    writil(ans);
}
int Tcn;
int main(){
    readi(Tcn);
    while(Tcn--)mian();
    return 0;
}
posted @ 2025-06-17 15:29  矞龙OrinLoong  阅读(27)  评论(0)    收藏  举报