HDU3972-求多余的两个数

首先膜拜:

Impossible itself says 1 M possible ------ Tourist’s quote


这个题目想了两天,今晚脑子一下热,想到了第一个做法TLE,然后又找到了另外一个做法AC,回头优化第一种做法AC了。

1、对V位处理法,进行二进制分解可以求出a+b

      这样再把把V*V二进制分解,可得出a*a+b*b

      综合a+b,a*a+b*b如此可求出结果!

2、与官方给的方法相同

  Case 1:两个数相同

   我们只要把所有数字的各个数位的二进制个数保存下来,模3即可

   模剩下的数组里面非零部分必定是2,把二进制还原即可

  Case 2:两个数不同

   两个数必定有一个位上面的二进制表示不同

   开一个[i][j]31*31的数组,意思是第i位是1的所有数字第j位为1的个数

   对数组里面的所有元素模3

 

   接着,有一个很显然的想法,两个数二进制不同,那么必定有一个[i][i]是1

   把i处保存的数还原,得到一个解

   然后还有一个数是不能用同样的方法来找的,因为可能a&b==a

   也就是说,这种方法只能找到b,找不到a

   找a其实只要把[i][i]为2的二进制个数全部减去b的二进制个数,

   生下来的就是a的,还原即可


我第一次做法TLE,开了一个数组[i][j]表示i位和j位同时处于一个数中的个数!这样每次处理一个数的时间为O(35*35)显然TLE!

改进优化以后便是官方的做法了!


TLE代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#define LL long long
#define m 35

int link[66][66];
int d[66];
int q[66];

int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        int n;
        memset(link,0,sizeof(link));
        memset(q,0,sizeof(q));
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            LL x;
            scanf("%I64d",&x);
            int len = 0;
            while (x)
            {
                len++;
                if (x&1)
                {
                    d[len]=1,q[len]=(q[len]+1)%3;

                }
                else
                    d[len]=0;
                x>>=1;
            }
            for (int i=1;i<=len;i++)
                for (int j=1;j<=len;j++)
                    if (i!=j && d[i]&&d[j])
                        {
                            link[i][j]=(link[i][j]+1)%3;
                            link[j][i]=(link[j][i]+1)%3;
                        }
        }
        LL a = 0,b = 0;
        for (int i=1;i<=m;i++)
        {
            if (q[i]==2)
            {
                a += (1<<(i-1));
                b += (1<<(i-1));
                for (int j=1;j<=m;j++)
                    link[i][j]=link[j][i]=0;
                q[i]=0;
            }
        }
     //   printf("%I64d %I64d\n",a,b);
        int k=1;
        while (k<=m && q[k]==0)
            k++;
        while (k<=m)
        {
            a += (1<<(k-1));
            q[k]=0;
            int t = 1;
            while (t<=m && link[k][t]==0)
                t++;
            k = t;
        }
        for (k = 1;k<=m;k++)
            if (q[k])
                b += (1<<(k-1));
        LL tmp ;
        if (a>b)
        {
            tmp = a;
            a = b;
            b = tmp;
        }
        printf("%I64d %I64d\n",a,b);
    }
    return 0;
}

优化后AC代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#define LL long long
#define m 37

int link[66][66];
int q[66];

int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        int n;
        memset(link,0,sizeof(link));
        memset(q,0,sizeof(q));
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            LL x;
            scanf("%I64d",&x);
            int len = 0;
            int head =-1;
            while (x)
            {
                len++;
                if (x&1)
                {
                    q[len]=(q[len]+1)%3;
                    if (head ==-1)
                        head = len;
                    else
                        link[head][len]=(link[head][len]+1)%3;
                }
                x>>=1;
            }

        }
        LL a = 0,b = 0;
        for (int i=1;i<=m;i++)
        {
            if (q[i]==2)
            {
                a += (1<<(i-1));
                b += (1<<(i-1));
            }
        }

      //  printf("%I64d %I64d\n",a,b);
        int k=1;
        while (k<=m && q[k]==0)
            k++;
        if (k<=m)
        {
        for (int i=1;i<=m;i++)
            if ((i==k&&q[i]==1) || (link[k][i]==1&&q[i]==1))
            {
                a += (1<<(i-1));
                q[i]=0;
            }
        }
      //  printf("%I64d %I64d\n",a,b);
        for (k = 1;k<=m;k++)
            if (q[k]==1)
                b += (1<<(k-1));
        LL tmp ;
        if (a>b)
        {
            tmp = a;
            a = b;
            b = tmp;
        }
        printf("%I64d %I64d\n",a,b);
    }
    return 0;
}


第一中做法代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#define LL long long
#define M 67

int a[M];
int b[M];

LL mysqrt(LL x)
{
    LL res = (LL)sqrt((double)x);
    while (res*res<x)
        res++;
    while (res*res>x)
        res--;
    return res;
}
int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        int n;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            LL x;
            scanf("%I64d",&x);
            LL t = x;
            int k = 0;
            while (t)
            {
                k++;
                if (t&1)
                    a[k]=(a[k]+1)%3;
                t>>=1;
            }
            t = x*x;
            k = 0;
            while (t)
            {
                k++;
                if (t&1)
                    b[k]=(b[k]+1)%3;
                t>>=1;
            }
        }
        LL x= 0,y = 0;
        LL c= 1;
        for (int i=1;i<=63;i++)
        {
            x+=a[i]*c;
            y+=b[i]*c;
            c<<=1;
        }
    //    printf("%I64d %I64d\n",x,y);
        LL t = mysqrt(2*y-x*x);
     //   printf("%I64d\n",t);
        printf("%I64d %I64d\n",(x-t)/2,(x+t)/2);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值