HDU 5014(Number Squence)



这道题一开始没有什么想法。后来队友找到了一些规律,试验了一下,发现规律成立,然后就AC了。

我们的做法是由n开始从大到小遍历,每到一个数w,就找出比它小的二次方数h(就是h=2^k),然后找到w关于h对称的数,进行组合,并且将组合过的数用vis数组标记一下,下一次遍历到的时候就不再操作。而二次方数h就和h-1进行组合。0如果没有被组合过,就是0和0组合。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define eps 1e-6
#define LL long long
#define ull unsigned long long
using namespace std;
int n,vis[100005];
struct point{
    int val,xval,ind;
}p[100005];
int a[20];
bool cmp(point a,point b)
{
    return a.val<b.val;
}
bool cmp1(point a,point b)
{
    return a.ind<b.ind;
}
int main()
{
    a[0]=1;
    for(int i=1;i<=20;i++)
    {
        a[i]=a[i-1]*2;
    }
    while(scanf("%d",&n)!=EOF)
    {
            for(int i=0;i<=n;i++)
            {
                scanf("%I64d",&p[i].val);
                p[i].ind=i;
                vis[i]=0;
            }
            LL ans=0;
            sort(p,p+n+1,cmp);
            for(int i=n;i>=0;i--)
            {
                int tem=(upper_bound(a,a+20,p[i].val)-a)-1;
                if(tem<0&&!vis[p[i].val]){p[i].xval=p[i].val;vis[p[i].val]=1;continue;}
                if(!vis[p[i].val]&&p[i].val==a[tem]&&!vis[p[i-1].val]){
                    ans+=(2*(p[i].val^p[i-1].val)+0LL);
                    vis[p[i].val]=vis[p[i-1].val]=1;
                    p[i].xval=p[i-1].val;
                    p[i-1].xval=p[i].val;
                }
                else if(!vis[p[i].val]&&p[i].val>a[tem]){
                    int temp=p[i].val-a[tem];
                    int x=a[tem]-temp-1;
                    if(!vis[x]){
                        ans+=(2*(p[i].val^x)+0LL);
                        vis[p[i].val]=vis[x]=1;
                        p[i].xval=x;
                        p[x].xval=p[i].val;
                    }
                }
            }
            printf("%I64d\n",ans);
            sort(p,p+n+1,cmp1);
            for(int i=0;i<n;i++)
                printf("%d ",p[i].xval);
            printf("%d\n",p[n].xval);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值