單調棧&可持久化0/1trie樹
題目描述
小w學會了RMQ算法,他現在可以求出一個給定數組某一段子區間的最大值,最小值。
在這之前,他也學會了前綴和,並且他知道前綴和可以擴展到位運算求出區間異或和。
現在你給了他一個長度大小為n的數組,為了考察小w寫RMQ以及前綴異或和的正確性,你要求他求出該數組的某一個子區間,記該子區間的異或和為xorsum,記該子區間的最大值為max,記該子區間的最小值為min,你要求使得xorsum⊕max⊕min最大。其中⊕為位運算異或操作。
輸入描述:
第一行輸入一個正整數n,表示數組的長度。
接下來一行n個非負整數a[i]表示數組中的內容。
輸出描述:
僅一行一個非負整數,表示xorsum⊕max⊕min的最大值。
樣例輸入
3
1 2 3
樣例輸出
3
題解
根據大佬Treaker的指示,我們知道我們需要枚舉最大值和最小值。所以我們先用單調棧來預處理每一個數右面第一個比自己大的數和第一個比自己小的數。時間復雜度為\(O(n)\).
然后我們在處理好之后就可以枚舉每一個子序列的左端點,由於在一定的范圍內子序列的最大值和最小值是不變的,所以我們可以利用我們處理好的單調棧來更新最小值和最大值。根據yych大佬的指示,在平均情況下(隨機數據下)我們需要"跳"(更新)\(log(n)\)次。
那么當我們確定了最大值和最小之后,怎么跟新我們的答案?這時候就要用到可持久化0/1trie樹了。設最大值的位置是\(maxx\),最小值的位置是\(minn\),第一個比\(a_i\)大的位置是\(da[i]\),第一個比\(a_i\)小的位置是\(xiao[i]\),\(yi[i]\)為前綴異或和,則我們需要在\(max(maxx,minn)\)~\(da(xiao)[maxx]-1\)找到一個\(yi[i]\)使其亦或上\(yi[i-1]\)^\(a[maxx]\)^\(a[minn]\)最大(想一想,為什么)。我們使用可持久化0/1trie樹就能在\(O(log(n))\)內找出最大值了。
這樣的時間復雜度是\(O(nlog^2(n))\),但是這里面還是存在一些問題,那就是當數據為單調遞增(減)的時候,單調棧更新最大值和最小值的時間復雜度就退化成了\(O(n)\),這樣的時間復雜度是\(O(n^2log(n))\).妥妥的\(TLE\),對於這樣數據我們直接判一下,我們發現就是求一段區間的異或和的最大值,直接用trie樹就OK了。這樣的時間復雜度為\(O(nlog(n))\).但這畢竟只能特判,在不知道的情況下還是會死掉的TAT。
實測最慢的為707 ms,還是比較優秀的(可能是我分析的時間復雜度不太准吧)。代碼還是很好打的。
最后獻上我丑陋的代碼。
#include<iostream>
#include<cstdio>
#define R register
using namespace std;
int n, top, ans, cnt, maxx, minn, f1, f2;
const int N = 100010;
int a[N], zhan[N], da[N], xiao[N], yi[N], rt[N];
int js[N * 50], tr[N * 50][2];
inline int read()
{
int res = 0; char ch = getchar(); bool XX = false;
for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
return XX ? -res : res;
}
void Insert(int k, int pre, int t, int x)
{
if (t < 0)return;
R int i = (x >> t) & 1;
tr[k][!i] = tr[pre][!i];
tr[k][i] = ++cnt;
js[tr[k][i]] = js[tr[pre][i]] + 1;
Insert(tr[k][i], tr[pre][i], t - 1, x);
}
int ask(int l, int r, int t, int x)
{
if (t < 0)return 0;
R int i = (x >> t) & 1;
if (js[tr[r][!i]] > js[tr[l][!i]])
return (1 << t) | ask(tr[l][!i], tr[r][!i], t - 1, x);
else return ask(tr[l][i], tr[r][i], t - 1, x);
}
inline void yych()
{
cin >> n; f1 = f2 = 1;
for (R int i = 1; i <= n; ++i)
{
a[i] = read();
yi[i] = a[i] ^ yi[i - 1];
rt[i] = ++cnt;
Insert(rt[i], rt[i - 1], 29, yi[i]);
}
for (R int i = 1; i <= n; ++i)da[i] = xiao[i] = n + 1;
top = 0;
for (R int i = 1; i <= n; ++i)
{
while (top && a[zhan[top]] < a[i])da[zhan[top--]] = i;
zhan[++top] = i;
}
top = 0;
for (R int i = 1; i <= n; ++i)
{
while (top && a[zhan[top]] > a[i])xiao[zhan[top--]] = i;
zhan[++top] = i;
}
for (int i = 2; i <= n; ++i)
{
if (a[i] < a[i - 1])f1 = 0;
if (a[i] > a[i - 1])f2 = 0;
}
}
void slove1()
{
for (int i = 1; i <= n; ++i)ans = max(ans, a[i]);
for (int i = 2; i <= n - 1; ++i)
ans = max(ans, ask(rt[i - 1], rt[n - 1], 29, yi[i - 1]));
}
int main()
{
yych();
if (f1 || f2)
{
slove1();
cout << ans;
return 0;
}
for (R int i = 1; i <= n; ++i)
{
maxx = minn = i;
while (maxx <= n && minn <= n)
{
if (da[maxx] <= xiao[minn])
{
ans = max(ans , ask(rt[max(maxx, minn) - 1] , rt[da[maxx] - 1] , 29 , yi[i - 1] ^ a[maxx] ^ a[minn]));
maxx = da[maxx];
} else
{
ans = max(ans , ask(rt[max(maxx, minn) - 1] , rt[xiao[minn] - 1] , 29 , yi[i - 1] ^ a[maxx] ^ a[minn]));
minn = xiao[minn];
}
}
}
cout << ans;
return 0;
}
