Problem Description
There is a special number sequence which has n+1 integers. For each number in sequence, we have two rules:
● ai ∈ [0,n]
● ai ≠ aj( i ≠ j )
For sequence a and sequence b, the integrating degree t is defined as follows(“⊕” denotes exclusive or):
t = (a0 ⊕ b0) + (a1 ⊕ b1) +···+ (an ⊕ bn)
(sequence B should also satisfy the rules described above)
Now give you a number n and the sequence a. You should calculate the maximum integrating degree t and print the sequence b.
Input
There are multiple test cases. Please process till EOF.
For each case, the first line contains an integer n(1 ≤ n ≤ 105), The second line contains a0,a1,a2,…,an.
Output
For each case, output two lines.The first line contains the maximum integrating degree t. The second line contains n+1 integers b0,b1,b2,…,bn. There is exactly one space between bi and bi+1(0 ≤ i ≤ n - 1). Don’t ouput any spaces after bn.
Sample Input
4
2 0 1 4 3
Output
20
1 0 2 3 4
解题思路
1.题意假设给定自然数序列ai,ai ∈ [0,n] ,ai ≠ aj( i ≠ j ),求自然数序列bi,使ai与bi对应位上的每一个数进行异或运算后相加和最大
2.怎么才能使异或运算之后相加和最大,显然采用贪心策略,使每一个对应位异或后能取得最大值。所以重点在于怎么求出与那位数异或能取得最大值。
3.例如序列0 1 2 3 4 5 6 7 8 9 10转化成二进制00000000 00000001 00000010 00000011 00000100 00000101 00000110 00000111 00001000 00001001 00001010,不难发现当转化为二进制时每一位互补时能取得最大值,如00001010和00000101,00001001和00000110,00001000和00000111,并且可以发现是在一个区间内两两对应,因为当5(00000101)和10(00001010)互补时,5+1(00000110)和10-1(00001001)也互补。
4.所以问题转化成怎么求互补数的问题,这里需要二进制的知识,首先找出第一个比给定数字n大的数,这个数必须是2的倍数,所以比10大的且是2的倍数的数是16(00010000),然后16-1(00001111),再将n按位取反~n(11110101)后进行按位与运算&,得到与n(10)互补的数c(5)(00000101),所以这样一个区间就解决了,剩下的只需要重复此过程就可以
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<memory.h>
#include<algorithm>
using namespace std;
const int N=100005;
int a[N],b[N];
long long int sum;
void f(int n){
if(n<=0){
return;
}
int k=0;
while((1<<k)<=n)k++;
//求比n大的数,这个数是2的倍数,10->16
//1<<k,就是2^k
int cur=1<<k;
int c=~n&(cur-1);//求与n互补的数
for(int i=n,j=c;i>j;j++,i--){
b[i]=j;
b[j]=i;
sum+=(i^j)*2;
}
if(cur!=0){//进行下一个区间
f(c-1);
}
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
sum=0;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<=n;i++){
scanf("%d",&a[i]);
}
f(n);
printf("%lld\n",sum);
for(int i=0;i<=n;i++){
printf("%d%c",b[a[i]],(i==n)?'\n':' ');
}
}
return 0;
}