[题解]CF1742G Orray

思路

做这道题之前,首先要知道一个性质:\(a \operatorname{or} b \geq a\)。那么,我们就能得出一个结论:经过一定顺序的排列,最多经过 \(\lceil \log_2^{a_{\max}} \rceil\) 个数就能让前缀或的值达到最大值。

我们不妨令有一个位置 \(i\)\(b_1,b_2,\dots,b_{i - 1}\) 单调递增,而 \(b_i = b_{i + 1} = \dots = b_n\)

那么,根据上面的结论,我们可知 \(i\) 的最大值一定为 \(\lceil \log_2^{a_{\max}} \rceil\)

因此,我们可以从 \(1\)\(30\) 枚举(因为 \(0 \leq a_i \leq 10^9\),所以,\(\lceil \log_2^{a_{\max}} \rceil = 30\)),每一次选出能使或值变得最大的一个数排在重拍后的 \(a\) 数组的前面(排在上一次选出的数的后面)。

但是,我们选的时候有可能有剩下的数,那该怎么办呢?由于,我们参考的是重拍后的 \(a\) 的前缀或和。既然这时的或和已经到了最大值,根据或的性质,后面无论怎么加数改它都不会变了。

其实,我们可以直接选出第一位的数,显而易见的是,第一位一定是为:\(a_{\max}\)。因为,\(0 \operatorname{or} a_{\max}\) 是最大的。

我们来分析一下时间复杂度为:\(\Theta(30NT)\)

Code

#include <bits/stdc++.h>  
#define re register  
  
using namespace std;  
  
const int N = 2e5 + 10;  
int T,n,idx,res;  
int arr[N],ans[N];  
  
inline int read(){  
    int r = 0,w = 1;  
    char c = getchar();  
    while (c < '0' || c > '9'){  
        if (c == '-') w = -1;  
        c = getchar();  
    }  
    while (c >= '0' && c <= '9'){  
        r = (r << 3) + (r << 1) + (c ^ 48);  
        c = getchar();  
    }  
    return r * w;  
}  
  
inline int cmp(int a,int b){  
    return a > b;  
}  
  
int main(){  
    T = read();  
    while (T--){  
        idx = res = 0;  
        n = read();  
        for (re int i = 1;i <= n;i++) arr[i] = read();  
        sort(arr + 1,arr + 1 + n,cmp);//排序便于找最大值   
        ans[++idx] = res = arr[1];//直接确定第一位   
        arr[1] = -1;//把取过的数标记为 -1   
        for (re int i = 1;i <= 31;i++){  
            int Max = res;  
            int id = -1;  
            for (re int j = 1;j <= n;j++){//暴力枚举   
                if (~arr[j]){//如果取过了便不能再取了   
                    if (Max < (res | arr[j])){//判断新的值是否能超过之前的最大值   
                        Max = res | arr[j];//是,更新   
                        id = j;//更新位置   
                    }  
                }  
            }  
            if (~id){//如果有数被取出过   
                res = Max;//更新当前的或值   
                ans[++idx] = arr[id];//添加到答案数组中   
                arr[id] = -1;//记得标记   
            }  
            else break;//直接提前结束   
        }  
        for (re int i = 1;i <= idx;i++) printf("%d ",ans[i]);//先输出被取出过的   
        for (re int i = 1;i <= n;i++){  
            if (~arr[i]) printf("%d ",arr[i]);//把没选出过的输出出来   
        }  
        puts("");  
    }  
    return 0;  
}  
posted @ 2024-06-24 12:22  WBIKPS  阅读(46)  评论(0)    收藏  举报