[HEOI2013]ALO
https://zybuluo.com/ysner/note/1238170
题面
现在你拥有\(n\)颗宝石,每颗宝石有一个能量密度,记为\(a_i\)。现在你可以选取连续的一些宝石(必须多于一个)进行融合,则融合而成的宝石的能量密度为这些宝石中能量密度的次大值与其他任意一颗宝石的能量密度按位异或的值。
现在你需要知道你怎么选取需要融合的宝石,才能使生成的宝石能量密度最大。
- \(n\leq5*10^4\)
题面
显然要可持久化\(Trie\)树。
唯一有点费脑子的地方是次大值。
在一个区间找次大值会把复杂度弄得很鬼。
换个角度想,如果枚举次大值,则有两种情况:
- 最大值在左边,右边数都比次大值小(右端点右一个比次大值大)
- 最大值在右边,左边数都比次大值小(左端点左一个比次大值小)
实际上我们可以用链表维护大小关系。
依\(a_i\)从小到大枚举,用完这个数就把它删掉,这样左右端就是两个比它大的数,可以\(O(1)\)确定区间左右端点。
于是分情况统计答案即可。
不加rt调1h系列
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=1e9+7,N=1e5+100;
struct dat{int w,id;bool operator < (const dat &o) const {return w<o.w;}}a[N];
int ysn,sum[N*50],t[N*50][2],l[N],r[N],ans,n,rt[N*50];
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void Build(re int x,re int &y,re int w,re int dep)
{
sum[y=++ysn]=sum[x]+1;
if(dep<0) return;
re int p=(w>>dep)&1;
t[y][p^1]=t[x][p^1];
Build(t[x][p],t[y][p],w,dep-1);
}
il int Query(re int x,re int y,re int w,re int dep)
{
if(dep<0) return 0;
re int p=(w>>dep)&1,tmp=sum[t[y][p^1]]-sum[t[x][p^1]];
if(tmp) return (1<<dep)+Query(t[x][p^1],t[y][p^1],w,dep-1);
else return Query(t[x][p],t[y][p],w,dep-1);
}
int main()
{
n=gi();
fp(i,1,n) a[i].w=gi(),a[i].id=i,l[i]=i-1,r[i]=i+1,Build(rt[i-1],rt[i],a[i].w,30);
sort(a+1,a+1+n);
fp(i,1,n)
{
re int x=a[i].id,z=l[x],y=r[x];
r[z]=y;l[y]=z;
if(z) ans=max(ans,Query(rt[l[z]],rt[y-1],a[i].w,30));
if(y<=n) ans=max(ans,Query(rt[z],rt[r[y]-1],a[i].w,30));
}
printf("%d\n",ans);
return 0;
}