【BZOJ3166】ALO(HEOI2013)-set+可持久化trie
测试地址:ALO
题目大意:给定一个数列,每项各不相同,令一个区间的价值为:区间中的次大值与区间中某个其它元素异或起来能得到的最大值,求最大的区间价值。
做法:本题需要用到set+可持久化trie。
我们知道,如果一个元素是次大值,那么我们肯定希望找到以这个元素为次大值的极大区间(即向左右任何一边扩展都不能满足条件的区间),这样就更有机会凑出更大的价值。以这个元素为次大值,等价于区间中存在且仅存在一个比它大的数字,那么显然这个元素不能是数列中最大的元素。然后我们找到这个元素左右离它最近的比它大的元素所处的位置,记为,再找到这个元素左右离它第二近的比它大的元素所处的位置,记为,不难发现以该元素为次大值的极大区间有两个:和。因此我们从大到小插入数列中的元素,然后用set维护求出上面那些位置(或者从小到大删除数列中的元素,用双向链表维护求出,这里本人偷懒使用了STL),就可以得到极大区间了。可以知道这样的区间至多有个。
那么现在问题就变成求一个区间中某个元素和某个给定的数异或起来能得到的最大值。我们把每个元素二进制分解,就成了一个串,如果我们能想办法快速求出一个区间中所有串组成的trie,我们就可以贪心求解了。这时我们类比可持久化线段树,建一棵可持久化trie,这样就可以提取出对应的区间了。
以上算法的时间复杂度为,可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,ch[2000010][2]={0},rt[50010]={0},siz[2000010]={0};
int tot;
set<int> S;
set<int>::iterator it;
struct forsort
{
int pos,val;
}f[50010];
void pushup(int v)
{
siz[v]=siz[ch[v][0]]+siz[ch[v][1]];
}
void insert(int last,int &v,int step,int x)
{
v=++tot;
siz[v]=siz[last];
ch[v][0]=ch[last][0];
ch[v][1]=ch[last][1];
if (step<0)
{
siz[v]++;
return;
}
if (x&(1<<step)) insert(ch[last][1],ch[v][1],step-1,x);
else insert(ch[last][0],ch[v][0],step-1,x);
pushup(v);
}
int query(int l,int r,int step,int x)
{
if (step<0) return 0;
if (x&(1<<step))
{
if (siz[ch[r][0]]-siz[ch[l][0]]>0)
return (1<<step)+query(ch[l][0],ch[r][0],step-1,x);
else return query(ch[l][1],ch[r][1],step-1,x);
}
else
{
if (siz[ch[r][1]]-siz[ch[l][1]]>0)
return (1<<step)+query(ch[l][1],ch[r][1],step-1,x);
else return query(ch[l][0],ch[r][0],step-1,x);
}
}
bool cmp(forsort a,forsort b)
{
return a.val>b.val;
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
insert(rt[i-1],rt[i],31,x);
f[i].pos=i,f[i].val=x;
}
sort(f+1,f+n+1,cmp);
}
int findans(int l,int r,int x)
{
return query(rt[l-1],rt[r],31,x);
}
void work()
{
S.insert(0);
S.insert(n+1);
int ans=0;
for(int i=1;i<=n;i++)
{
int l1,l2,r1,r2;
it=S.lower_bound(f[i].pos);
r1=(*it);
it++;
if (it!=S.end()) r2=(*it);
else r2=r1;
it--;it--;
l1=(*it);
if (it!=S.begin()) it--,l2=(*it);
else l2=l1;
S.insert(f[i].pos);
if (i>1) ans=max(ans,max(findans(l2+1,r1-1,f[i].val),findans(l1+1,r2-1,f[i].val)));
}
printf("%d",ans);
}
int main()
{
init();
work();
return 0;
}