[题解]CF1742G Orray
思路
做这道题之前,首先要知道一个性质:。那么,我们就能得出一个结论:经过一定顺序的排列,最多经过 个数就能让前缀或的值达到最大值。
我们不妨令有一个位置 , 单调递增,而 。
那么,根据上面的结论,我们可知 的最大值一定为 。
因此,我们可以从 到 枚举(因为 ,所以,),每一次选出能使或值变得最大的一个数排在重拍后的 数组的前面(排在上一次选出的数的后面)。
但是,我们选的时候有可能有剩下的数,那该怎么办呢?由于,我们参考的是重拍后的 的前缀或和。既然这时的或和已经到了最大值,根据或的性质,后面无论怎么加数改它都不会变了。
其实,我们可以直接选出第一位的数,显而易见的是,第一位一定是为:。因为, 是最大的。
我们来分析一下时间复杂度为:。
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;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18264809
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】