Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Every value appears exactly twice. In a palindrome, if some occurrence sits at position away from the center, where must the other copy of that value sit?
Let be the other occurrence of . If a palindromic subarray is and , then every non-center position must satisfy . So the quantity is screaming for attention.
Fix a center-sum . Then all non-center positions inside the palindrome must have . So for a fixed center, the maximal palindrome is just the longest consecutive stretch around that center where this condition keeps holding. Odd centers have one free middle cell.
Build and precompute maximal runs of equal . From those runs, you can generate in all maximal palindromes: one for every odd center, and one for every even center that actually works.
Now the problem is only: among intervals , find the maximum . Do range mex offline: sweep the right endpoint , maintain last occurrences of all values in a segment tree over values , and for query return the smallest with .
This problem looks like palindrome + mex, so your first instinct might be Manacher, hashes, black magic, or some other decorative nonsense. But the condition that every value appears exactly twice absolutely kills the problem.
Let and use -based indexing. For every position , define as the position of the other occurrence of .
Now take a palindromic subarray and set
For every non-middle position , its mirrored position is , so we must have
But each value exists only twice in the whole array. So if is not the center of an odd-length palindrome, the other occurrence of is forced to be exactly . Therefore
Define
Then in any palindrome with center-sum , every non-center position inside it must satisfy
That is the whole damn trick.
For a fixed center, taking a larger palindrome cannot decrease mex. So for each center, we only care about the maximal palindrome around it.
Here . Position is the middle, so it is free: we do not require .
For every other included position , we need
So the palindrome expands exactly while the positions immediately to the left of keep having value in the array .
If the longest consecutive block ending at with value starts at , then the maximal odd palindrome is
If or , then it is just .
Why is the right end automatically ? Because means
so every left position forces its mirrored right position.
Also note that always, because . So the equal-value block cannot pass through the center. Nice and clean.
Now , and there is no free middle position. So every position in the palindrome must satisfy
Hence the maximal even palindrome is simply the maximal consecutive run of value in that contains position .
This run exists iff
So after building maximal equal-value runs of , we can generate all candidate maximal palindromes in total:
So there are only intervals to check.
Now we just need the mex of each candidate interval .
This is a standard offline trick.
Sweep the array from left to right by right endpoint . For every value , maintain
Then appears in iff
Therefore
So we keep all in a segment tree over the value axis :
We include a dummy value that never appears. So if are all present, the query naturally returns .
This gives , where is the number of candidate intervals.
Compute .
Now build two helper arrays for the equal-value runs of :
Both are easy in linear time.
Then:
That is it. No DP circus, no binary-search yoga, no suffering.
A subarray is a palindrome iff for every ,
Proof. The mirrored position of inside is . Palindromicity means the values there are equal. Since every value has exactly two occurrences in the whole array, the second occurrence of is unique, so it must be exactly . The converse is immediate.
For a fixed center, the interval constructed above is exactly the maximal palindrome around that center.
Proof. By Lemma 1, all non-center positions must satisfy , where is the center-sum. So the palindrome extends exactly through consecutive positions around the center where , and it must stop at the first position where this fails. That is precisely how the interval is constructed.
It is enough to consider only maximal palindromes around each center.
Proof. Any smaller palindrome with the same center is contained in the maximal one. Adding elements cannot remove existing values, so mex cannot decrease.
The offline segment tree query returns the mex of every interval correctly.
Proof. After processing right endpoint , is the latest occurrence of in . Thus appears in iff . So the smallest with is exactly the mex.
By Lemmas -, we enumerate every relevant maximal palindrome, compute its mex correctly, and take the maximum. Done.
Let .
So the total complexity per test case is
with memory.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct SegTree {
int n;
static constexpr int INF = 1e9;
vector<int> mn;
void init(int sz) {
n = 1;
while (n < sz) n <<= 1;
mn.assign(2 * n, INF);
for (int i = 0; i < sz; ++i) mn[n + i] = 0;
for (int i = n - 1; i >= 1; --i) mn[i] = min(mn[2 * i], mn[2 * i + 1]);
}
void update(int p, int v) {
p += n;
mn[p] = v;
for (p >>= 1; p; p >>= 1) mn[p] = min(mn[2 * p], mn[2 * p + 1]);
}
int first_less(int need) const {
int x = 1, l = 0, r = n;
while (r - l > 1) {
int mid = (l + r) >> 1;
if (mn[2 * x] < need) {
x = 2 * x;
r = mid;
} else {
x = 2 * x + 1;
l = mid;
}
}
return l;
}
};
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int m = 2 * n;
vector<int> a(m + 1);
vector<int> p1(n, -1), p2(n, -1);
for (int i = 1; i <= m; ++i) {
cin >> a[i];
int x = a[i];
if (p1[x] == -1) p1[x] = i;
else p2[x] = i;
}
vector<int> mate(m + 1), b(m + 1);
for (int x = 0; x < n; ++x) {
mate[p1[x]] = p2[x];
mate[p2[x]] = p1[x];
}
for (int i = 1; i <= m; ++i) b[i] = i + mate[i];
vector<int> L(m + 1), R(m + 1);
L[1] = 1;
for (int i = 2; i <= m; ++i) {
L[i] = (b[i] == b[i - 1] ? L[i - 1] : i);
}
R[m] = m;
for (int i = m - 1; i >= 1; --i) {
R[i] = (b[i] == b[i + 1] ? R[i + 1] : i);
}
vector<vector<int>> queries(m + 1);
auto add_query = [&](int l, int r) {
queries[r].push_back(l);
};
for (int c = 1; c <= m; ++c) {
int l = c, r = c;
if (c > 1 && b[c - 1] == 2 * c) {
l = L[c - 1];
r = 2 * c - l;
}
add_query(l, r);
}
for (int c = 1; c < m; ++c) {
if (b[c] == 2 * c + 1) {
add_query(L[c], R[c]);
}
}
SegTree st;
st.init(n + 1); // values 0..n, where n is a dummy missing value
int ans = 0;
for (int r = 1; r <= m; ++r) {
st.update(a[r], r);
for (int l : queries[r]) {
ans = max(ans, st.first_less(l));
}
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct SegTree {
int n;
static constexpr int INF = 1e9;
vector<int> mn;
void init(int sz) {
n = 1;
while (n < sz) n <<= 1;
mn.assign(2 * n, INF);
for (int i = 0; i < sz; ++i) mn[n + i] = 0;
for (int i = n - 1; i >= 1; --i) mn[i] = min(mn[2 * i], mn[2 * i + 1]);
}
void update(int p, int v) {
p += n;
mn[p] = v;
for (p >>= 1; p; p >>= 1) mn[p] = min(mn[2 * p], mn[2 * p + 1]);
}
int first_less(int need) const {
int x = 1, l = 0, r = n;
while (r - l > 1) {
int mid = (l + r) >> 1;
if (mn[2 * x] < need) {
x = 2 * x;
r = mid;
} else {
x = 2 * x + 1;
l = mid;
}
}
return l;
}
};
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int m = 2 * n;
vector<int> a(m + 1);
vector<int> p1(n, -1), p2(n, -1);
for (int i = 1; i <= m; ++i) {
cin >> a[i];
int x = a[i];
if (p1[x] == -1) p1[x] = i;
else p2[x] = i;
}
vector<int> mate(m + 1), b(m + 1);
for (int x = 0; x < n; ++x) {
mate[p1[x]] = p2[x];
mate[p2[x]] = p1[x];
}
for (int i = 1; i <= m; ++i) b[i] = i + mate[i];
vector<int> L(m + 1), R(m + 1);
L[1] = 1;
for (int i = 2; i <= m; ++i) {
L[i] = (b[i] == b[i - 1] ? L[i - 1] : i);
}
R[m] = m;
for (int i = m - 1; i >= 1; --i) {
R[i] = (b[i] == b[i + 1] ? R[i + 1] : i);
}
vector<vector<int>> queries(m + 1);
auto add_query = [&](int l, int r) {
queries[r].push_back(l);
};
for (int c = 1; c <= m; ++c) {
int l = c, r = c;
if (c > 1 && b[c - 1] == 2 * c) {
l = L[c - 1];
r = 2 * c - l;
}
add_query(l, r);
}
for (int c = 1; c < m; ++c) {
if (b[c] == 2 * c + 1) {
add_query(L[c], R[c]);
}
}
SegTree st;
st.init(n + 1); // values 0..n, where n is a dummy missing value
int ans = 0;
for (int r = 1; r <= m; ++r) {
st.update(a[r], r);
for (int l : queries[r]) {
ans = max(ans, st.first_less(l));
}
}
cout << ans << '\n';
}
return 0;
}