题目大意:
给你一个长度为n的序列,问你,这个序列的所有连续子序列中,子序列的最大值和次大值的异或和最大是多少?
分析:
我们直接枚举所有连续子序列肯定布星,我们换一个枚举方向。
我们考虑枚举子序列的次大值,那么,我们只需要找到一个当前枚举的点的值大的那个点作为最大值即可。
我们注意到,因为我们枚举的是次大值,所以它所处在的子序列中,比它大的点有且只有一个。所以,我们找到这个点左边,离它最近的比它大的点的值和这个点右边,离它最近的比它大的点的值,那么连续子序列的最大值必然是这两个值中的一个。我们每个都算下取最大即可。
至于如果求这两个值,随便搞搞就行,基本就不需要动脑了/x
我是用的st表+二分搞的。
代码:
class Solution { public: int s[100001][20]; inline int calc(int l,int r){ int len=(r-l+1),now=0; while(len>=(1<<(now+1))){ ++now; } return max(s[l][now],s[r-(1<<now)+1][now]); } int solve(int n, vector<int>& a) { for(int i=0;i<n;++i){ s[i][0]=a[i]; } int lon=1; for(int i=1;i<20;++i){ lon<<=1; for(int j=0;j<n;++j){ int r=j+lon-1; if(r>n){ break; } s[j][i]=max(s[j][i-1],s[r-(lon/2)+1][i-1]); } } int ans=0; for(int i=0;i<n;++i){ int l=0,r=i-1,res=-1; while(l<=r){ int mid=(l+r)>>1; if(calc(mid,i-1)>a[i]){ res=mid;l=mid+1; }else{ r=mid-1; } } if(res!=-1){ ans=max(ans,(a[i]^a[res])); } l=i+1,r=n-1,res=-1; while(l<=r){ int mid=(l+r)>>1; if(calc(i+1,mid)>a[i]){ res=mid,r=mid-1; }else{ l=mid+1; } } if(res!=-1){ ans=max(ans,(a[i]^a[res])); } } return ans; } };
我们还可以使用单调队列,这样,复杂度就只有了!
代码:
class Solution { public: int que[100001],l=1,r=0; int solve(int n, vector<int>& a) { que[++r]=a[1];int ans=0; for(int i=2;i<=n;++i){ while(l<=r&&que[r]<=a[i]){ --r; } if(l<=r){ ans=max(ans,(a[i]^que[r])); } que[++r]=a[i]; } l=1,r=0; que[++r]=a[n]; for(int i=n-1;i;--i){ while(l<=r&&que[r]<=a[i]){ --r; } if(l<=r){ ans=max(ans,(a[i]^que[r])); } que[++r]=a[i]; } return ans; } };
事实上,只需要使用单调栈就行了qwq
代码:
class Solution { public: int sta[100001],top=0; int solve(int n, vector<int>& a) { sta[++top]=a[1];int ans=0; for(int i=2;i<=n;++i){ while(top&&sta[top]<=a[i]){ --top; } if(top){ ans=max(ans,(a[i]^sta[top])); } sta[++top]=a[i]; } top=0; sta[++top]=a[n]; for(int i=n-1;i;--i){ while(top&&sta[top]<=a[i]){ --top; } if(top){ ans=max(ans,(a[i]^sta[top])); } sta[++top]=a[i]; } return ans; } };