BZOJ3166 [Heoi2013]Alo 可持久化Trie

xiaoxiao2021-02-28  117

题意:给定长度为n的整数序列,一个连续子段的收益为子段次大值xor子段中任意数,求最大收益子段值

Sol:

考虑枚举次大值是谁,则一个数作为次大值时的扩张范围是[左边第二个大于她的数+1 , 右边第一个大于她的数-1] 和 [左边第一个大于她的数+1 , 右边第二个大于她的数-1] 

两个区间可以合并起来,用可持久化Trie查询最大异或值。

找左右数可以将所有数从小到大排序后顺序枚举,每次插入到set里查询

也可以用双向链表维护,每次删除即可

Code:

#include<bits/stdc++.h> #define debug(x) cout<<#x<<"="<<x<<endl using namespace std; const int maxn = 50009; const int Log = 30; const int inf = 0x3f3f3f3f; int n,mx,ans; int pre[maxn],nxt[maxn],rt[maxn]; struct Trie { int son[maxn*Log*2][2],cnt[maxn*Log*2]; int tot; inline int insert(int x,int num) { int tmp,y;tmp=y=++tot; for(int i=Log;i>=0;i--) { son[y][0]=son[x][0];son[y][1]=son[x][1]; cnt[y]=cnt[x]+1; int wh=(num>>i)&1; son[y][wh]=++tot; x=son[x][wh];y=son[y][wh]; }cnt[y]=cnt[x]+1; return tmp; } inline int query(int l,int r,int num) { int res=0; for(int i=Log;i>=0;i--) { int wh=(num>>i)&1; if(cnt[son[r][wh^1]]-cnt[son[l][wh^1]]) res|=(1<<i),r=son[r][wh^1],l=son[l][wh^1]; else r=son[r][wh],l=son[l][wh]; } return res; } }uuz; struct node{int pos,val;}a[maxn]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline bool cmp(const node &x,const node &y) { return x.val<y.val; } int main() { n=read(); for(int i=1;i<=n;i++) { a[i]=(node){i,read()}; rt[i]=uuz.insert(rt[i-1],a[i].val); } for(int i=1;i<=n;i++) pre[i]=i-1,nxt[i]=i+1; pre[0]=0;nxt[n+1]=n+1;nxt[0]=1;pre[n+1]=n; sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) { int now=a[i].pos; int L=pre[now],R=nxt[now]; int l=pre[L],r=nxt[R]; if(l+1<=R-1) ans=max(ans,uuz.query(rt[l],rt[R-1],a[i].val)); if(L+1<=r-1) ans=max(ans,uuz.query(rt[L],rt[r-1],a[i].val)); nxt[pre[now]]=nxt[now]; pre[nxt[now]]=pre[now]; } cout<<ans; return 0; }

转载请注明原文地址: https://www.6miu.com/read-29855.html

最新回复(0)