一道很关于位运算的很好的构造题,纠正了我先前对位运算的一些误解。
确定一个 permutation(n) ,使得:
(初始),从 对 依次进行操作。如果 是奇数 & ,否则 | 。
之前认为,与和或结合的结合的位运算是具有结合律的,也就是可以先把与的部分算出来,再或起来。
但实际上这是错误的,只能从左到右依次计算,checker如下 :
| #include<bits/stdc++.h> |
| using namespace std; |
| const int N=1e5+10; |
| int a[N]; |
| bool check(int n) |
| { |
| int k=0; |
| for(int i=1;i<=n;++i) |
| { |
| if(i&1)k&=a[i]; |
| else k|=a[i]; |
| } |
| int cmp=0; |
| for(int i=2;i<=n-1;i+=2) |
| cmp|=a[i]&a[i+1]; |
| return k==cmp; |
| } |
| int main() |
| { |
| int n=9; |
| for(int i=1;i<=n;++i)a[i]=i; |
| int T=100; |
| do |
| { |
| cout<<check(n)<<endl; |
| }while(next_permutation(a+1,a+n+1)); |
| return 0; |
| } |
但是很容易可以想到一个贪心的做法,发现样例里面:
所有奇数 能得到的最大数就是他本身
所有偶数 能得到的最大数是把它本身的 全部变成 后的结果
所以不难想到分 的奇偶性讨论。
如果 为奇数,那么最后一位一定是 & ,最后的答案就一定小于等于我们放在最后一位的数。
所以贪心地把最大的 放在最末尾,尝试让前面的 个运算凑出 这个数值。
可以观察发现 这个偶数实际上和 只差了最后一位的 ,那么最后几位只需要是 :
这时候最后一位是 | ,那么为了补齐所有的 ,我们尝试在前面 个凑出除了最高位,其他都是 的数。
这一点只需要顾名思义地来做就可以了,然后要注意到 当 是 的若干次幂的时候,我们在前 个要凑出的数就是
| #include<bits/stdc++.h> |
| using namespace std; |
| template<typename T>inline void re(T &x) |
| { |
| x=0;int f=1;char c=getchar(); |
| while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} |
| while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} |
| x*=f; |
| } |
| template<typename T>inline void wr(T x) |
| { |
| if(x<0)putchar('-'),x=-x; |
| if(x>9)wr(x/10); |
| putchar(x%10^48); |
| } |
| inline void debug(int x){wr(x),putchar('\n');} |
| int n,m,T; |
| const int N=2e5+10; |
| int a[N]; |
| int check() |
| { |
| int k=0; |
| for(int i=1;i<=n;++i) |
| { |
| if(i&1)k&=a[i]; |
| else k|=a[i]; |
| } |
| printf("check:");debug(k); |
| } |
| inline void solve() |
| { |
| if(n&1) |
| { |
| debug(n); |
| for(int i=1;i<=n;++i)a[i]=i; |
| swap(a[1],a[n-3]); |
| } |
| else |
| { |
| int cnt=0,tmpn=n; |
| while(tmpn) |
| { |
| cnt++; |
| tmpn>>=1; |
| } |
| debug((1<<cnt)-1); |
| int res=((1<<cnt)-1)/2; |
| if(1<<(cnt-1)!=n) |
| { |
| for(register int i=1;i<=n-3;++i) |
| if(i==res)a[i]=n-2; |
| else a[i]=i; |
| a[n-2]=n-1,a[n-1]=n,a[n]=res; |
| } |
| else |
| { |
| for(int i=1;i<=n;++i)a[i]=i; |
| swap(a[1],a[n-4]),swap(a[3],a[n-3]); |
| } |
| } |
| for(int i=1;i<=n;++i)wr(a[i]),putchar(' '); |
| putchar('\n'); |
| } |
| int main() |
| { |
| re(T); |
| while(T--) |
| { |
| re(n); |
| solve(); |
| |
| } |
| return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效