2018 山东冬令营 中国石油 划分

5794: 划分

时间限制: 1 Sec   内存限制: 128 MB
提交: 18   解决: 6
[ 提交][ 状态][ 讨论版]

题目描述

给出n个不超过m的非负整数,将数划分成两个集合,记为1号集合和2号集合。x1为1号集合中所有数的异或和,x2为2号集合中所有数的异或和。在最大化x1+x2的前提下,最小化x1。

输入

第一行n
第二行n个非负整数

输出

一行两个数,第一个数是x1,第二个数是x2

样例输入

7
1 1 2 2 2 3 3

样例输出

1 3

提示

对于 30%的数据,n<=10
对于 60%的数据,n<=1000
对于 100%的数据,n≤105,m≤1018

来源

2018山东冬令营

赛后补了线性基的相关知识,发现自己还是跟SB一样不太懂线性基,但是大概懂了线性基的一个用法。

推荐线性基博客:http://www.cnblogs.com/ljh2000-jump/p/5869991.html

题解:

先求出所有数的异或和。从高位向低位枚举,若这一位是0,则考虑在划分集合的时候能否使每个集合中这两个数都是1。再从高位向低位枚举,若这一位是1,则考虑能否让x1的这一位是0。
划分为两个集合,相当于选出x1。枚举到一位时,要将所有这一位是1的数都异或一个出现过的这一位是1的数。这样保证剩下的数都小于当前枚举到的那一位,便于求解。(线性基)

我感觉是求出来一个线性基,尽量让那些大的都给x2,然后在给x1,就像上边说的那样如果0对应2个1这样的是肯定跑不了的。

long long a[100005];
long long b[100];
long long ans;
long long x1,x2; 
int n;
void solve()
{
    for(int i=1;i<=n;i++)
    {
        int flag=1;
        long long t=a[i];
        for(int j=63;j>=0;j--)
        {
            if(((t>>j)&1) && (!((ans>>j)&1)))//当前这个数的j位是1  但是 异或和是0 表示 是1 1 一定有贡献 
            {
                if(b[j])
                    t=t^b[j];
                else
                {
                    b[j]=t;
                    flag=0;
                    break;
                } 
            }
        }
        if(!flag)
            continue;
        //如果没找到上边的那种情况
        for(int j=63;j>=0;j--)//去寻找那种 ans是1的而且当前也是1的  存一下
        {
            if(((t>>j)&1) && ((ans>>j)&1))
            {
                if(b[j])
                    t=t^b[j];
                else
                {
                    b[j]=t;
                    break;
                }
            }
             
        } 
    }
    x1=0;
    x2=0;
    for(int j=63;j>=0;j--)//先去寻找那些  ans==0  而且x2当前第J位是0的  然后看下当前b[j]是否有数  有的话就加上
    {
        if((!((ans>>j)&1) && (!((x2>>j)&1))))
        {
            if(b[j])
            {
                x2=x2^b[j];
            }
        }
    }
    for(int i=63;i>=0;i--)
    {
        if(((ans>>i)&1)&&(!((x2>>i)&1)))//去找ans是1的  而且x2的j位还没有的加上  跟上边不一样 上边是0分解11的  这个是1分10的
        {
            if(b[i])
                x2=x2^b[i];
        }
    }
    x1=ans^x2;
}
int main()
{
    ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]); 
        ans=ans^a[i];
    }
    solve();
    printf("%lld %lld\n",x1,x2);
    return 0;
} 

你可能感兴趣的:(线性基)