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
*/
posted @ 2024-10-28 18:55  Hanggoash  阅读(23)  评论(0编辑  收藏  举报
动态线条
动态线条end