sgu275 To xor or not to xor

To xor or not to xor

给你 n 个非负整数序列 A1, A2, ..., AN。你需要寻找这个序列的一个子序列:Ai1, Ai2, ..., Aik (1<=i1 <i2 <...<ik <=N)满足Ai1 XORAi2 XOR...XORAik最大。

Xor 即为不进位的二进制加法。满足: 0+0=0

1+0=1 1+1=0

在 PASCAL 中,你可以使用 a:=b xor c 语句。其中 a,b,c 均为整型。 输入格式:(xor.in)

第一行包含一个正整数 n,第二行包含整数序列 A1, A2, ..., AN。 输出格式:(xor.out)

包含一个整数,表示 Ai1 XOR Ai2 XOR ... XOR Aik 的最大可能值。

样例输入:

3
11 9 5

样例输出:

14

数据规模:
对于 30%的数据,n<=10
对于 60%的数据,n<=20,ai<=1023
对于 100%的数据,0 <= Ai <= 10^18,0<n<=100。

思路:我们要求的是子序列异或起来的最大值,而0 <= Ai <= 10^18刚好可以用long long存下,

所以可以把每一个Ai看作二进制数,尽量使ans的高位是1,这样就可以保证求出来的是最大值.

开始在判断最高位(1<<63)时非常简单,

只要O(n)扫一遍,每次判断Ai在这一位(1<<63)是不是1就可以,只要有一个Ai这一位是1就可以满足.

但是马上就会发现选不同的数异或都可以使这一位(1<<63)是1,但是之后不一定最优.

这个时候就要给ans一个反悔的机会.

怎么样反悔呢?

怎么样能在异或了Ai之后枚举下一个二进制位时消除Ai的影响又不影响高位呢(相当于异或了别的数)?

(这里很不好想,我卡这儿半天,各位可以思考一下再看)

(结合一下异或的性质)

 

 

 

 

 

 

 

 

我们知道A^B^B仍然等于A,而且异或满足结合律

所以在Ans^Ai之后反悔的话,只要把其他这一二进制位是1的Aj都^Ai就可以啦!

这样Ans^Ai^Aj^Ai-->Ans^Aj,这就相当于异或了别的数啦~

选多个Ai也是一样的,在异或Ak之后再选的话,肯定要选偶数个才能保证最优,

偶数个异或起来也是可以消掉的并不影响答案.

 

一些细节:枚举的到Aj时要判断一下当前ans这一位是不是1,如果是的话才将ans^Aj

但是不管ans这一位是什么,只要Aj这一位是1,就要将其他Ak^Aj,这样才能有足够的反悔机会,不会挤掉答案

感觉有点像dp的二进制优化......?

还有Ak^Aj的时候不能一个for异或下去,要不然j==k的时候Aj就成0了,之后的Ak不会改变,就会WA

最后Aj赋成0就行

(这么傻的错误应该只有我会犯了吧qwq)

 

来跑个小样例~

(对齐有点问题所以直接截图了)

 

 

 

下面是代码

#include<cstdio>
#define ll long long
using namespace std;
int n;
ll num[101];
int main(){
    // freopen("xor.in","r",stdin);
    // freopen("xor.out","w",stdout);
    ll ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&num[i]);
    for(int i=63;i>=0;i--)//从高位开始枚举
        for(int j=1;j<=n;j++)
            if(num[j]&(1LL<<i)){//a[j]这一位是1
                if((ans&(1LL<<i))==0)//ans这一位是0
                    ans^=num[j];
                for(int k=1;k<=n;k++)//为保证之后最优
                    if((num[k]&(1LL<<i))&&k!=j)
                        num[k]^=num[j];
                num[j]=0;
            }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-07-22 15:49  AL76  阅读(270)  评论(0编辑  收藏  举报