【USACO6.1.3】Cow XOR奶牛异或 trie

来源:互联网 发布:小米2s改装4g网络 编辑:程序博客网 时间:2024/05/29 11:38

NKOJ 1873 奶牛异或

问题描述

农民约翰在喂奶牛的时候被另一个问题卡住了。他的所有N(1 <= N <= 100,000)个奶牛在他面前排成一行(按序号1..N的顺序),按照它们的社会等级排序。奶牛#1有最高的社会等级,奶牛#N最低。每个奶牛同时被指定了一个不唯一的附加值,这个数在0..2^21 - 1的范围内。
帮助农民约翰找出应该从哪一头奶牛开始喂,使得从这头奶牛开始的一个连续的子序列上,奶牛的附加值的异或最大。
如果有多个这样的子序列,选择结尾的奶牛社会等级最高的。如果还不唯一,选择最短的。

输入格式

第1行:一个单独的整数N。
第2到N + 1行:N个0..2^21 - 1之间的整数,代表每头奶牛的被赋予的数。第j行描述了社会等级j - 1的奶牛。

输出格式

第 1 行: 3个空格隔开的整数,分别为:最大的异或值,序列的起始位置、终止位置。

样例输入

5
1
0
5
4
2

样例输出

6 4 5


对于异或,由于这是一个按位的运算方式,也就是说,这一位的结果不会对其他的位造成任何影响。基于这个性质,我们可以贪心处理下面的问题:

如果给你一个数a和其他一些数,如何求a与其他数中的一个异或起来的最大值?

根据贪心原则,把所有数写成二进制的形式后,从高位开始讨论。那么a异或起来更大的显然是较高位上与a不相同的。

现在考虑这个问题。连续一段数的异或和,容易想到用前缀异或和处理。对于一个固定的右端点,如何找到左边与它异或起来最大的前缀异或和?暴力会使整个算法是O(N2)的,显然不可取。

正确的做法是把所有前缀异或和在写成二进制后插入到一个trie里。根据trie的性质,每一条从根节点出发的由上到下的路径都是一个前缀异或和。根据前面的贪心原则,设当前讨论数的某一位为x,那么如果当前trie的节点有x^1这个儿子,那么选择这个儿子走下去肯定是更优的,否则就只好走x这个儿子。按照这个方法走下去之后就在O(len)的复杂度里找到最优值。


很早之前的题了,代码有点丑,可以参照另一道类似的题的代码。

#include<stdio.h>#include<iostream>#include<algorithm>using namespace std;struct node{int son[2],num;}trie[2000005];int n,i,tot=1,sum[100005],ans,a=1,b=1;void ins(int c,int k){    int i,t,p=1;    for(i=20;i>=0;i--)    {        t=(c>>i)&1;        if(trie[p].son[t]==0)        {            trie[p].son[t]=++tot;            p=tot;            trie[p].num=0;        }        else p=trie[p].son[t];    }    trie[p].num=k;}int f(int c){    int i,t,p=1;    for(i=20;i>=0;i--)    {        t=(c>>i)&1;        if(trie[p].son[1^t]==0)p=trie[p].son[t];        else p=trie[p].son[1^t];    }    return trie[p].num;}main(){    int x,i,tt;    scanf("%d",&n);    ins(0,0);    for(i=1;i<=n;i++)    {        scanf("%d",&x);        sum[i]=sum[i-1]^x;        ins(sum[i],i);        tt=f(sum[i]);        if((sum[i]^sum[tt])>ans)        {            a=tt+1;b=i;ans=sum[i]^sum[tt];        }    }    cout<<ans<<" "<<a<<" "<<b;}