HDU 5014 Number Sequence(二进制+区间贪心)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值