CF2035C
CF2035C
一道很关于位运算的很好的构造题,纠正了我先前对位运算的一些误解。
分析
确定一个 permutation(n) ,使得:
\(k=0\) (初始),从 \(1-n\) 对 \(k\) 依次进行操作。如果 \(k\) 是奇数 \(k\)&\(a_i\) ,否则 \(k\)|\(a_i\) 。
之前认为,与和或结合的结合的位运算是具有结合律的,也就是可以先把与的部分算出来,再或起来。
但实际上这是错误的,只能从左到右依次计算,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;
}
但是很容易可以想到一个贪心的做法,发现样例里面:
所有奇数 \(n\) 能得到的最大数就是他本身
所有偶数 \(n\) 能得到的最大数是把它本身的 \(0\) 全部变成 \(1\) 后的结果
所以不难想到分 \(n\) 的奇偶性讨论。
Case odd
如果 \(n\) 为奇数,那么最后一位一定是 & ,最后的答案就一定小于等于我们放在最后一位的数。
所以贪心地把最大的 \(n\) 放在最末尾,尝试让前面的 \(n-1\) 个运算凑出 \(n\) 这个数值。
可以观察发现 \(n-1\) 这个偶数实际上和 \(n\) 只差了最后一位的 \(1\) ,那么最后几位只需要是 :
\(1,n-2,n-1,n\)
Case even
这时候最后一位是 | ,那么为了补齐所有的 \(0\) ,我们尝试在前面 \(n-1\) 个凑出除了最高位,其他都是 \(1\) 的数。
这一点只需要顾名思义地来做就可以了,然后要注意到 当 \(n\) 是 \(2\) 的若干次幂的时候,我们在前 \(n-1\) 个要凑出的数就是 \(n-1\)
Code
#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();
// check();
}
return 0;
}
/*
7
5
6
7
8
9
10
16
*/
为什么要练,为什么要写?
引用一句让我幡然悔悟的话:
“练了不一定写的出来正解,不练一定写不出来正解”
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18511370