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.
A good array is not about the current order. Sort it mentally. What property do its minimum, maximum, and duplicates have?
If a segment has length and is good, then its values are exactly some block
So every useful subsegment can be described by two numbers: its length and its value-block start .
Now take two good arrays of the same length . Their concatenation is good only if their two value blocks are adjacent: one is , the other is .
Brute-force all subsegments in . While extending a left endpoint, maintain , , and seen values. A subsegment is good iff it has no duplicate and
For each pair , store the minimum and maximum starting position of any good subsegment whose values are . For adjacent blocks and , two occurrences are non-overlapping iff
That’s the whole problem. No dark magic, just bookkeeping.
An array of length is good iff after rearranging it becomes
So it must contain distinct values, and its range must have size :
Equivalently, every good segment is a permutation of some value block .
Suppose we choose two good arrays of length .
Their value blocks are:
and
The concatenation has length and must also be good, so its values must be exactly one consecutive block of length .
Two length- consecutive blocks can form such a block only if they are adjacent. So, after possibly swapping them:
That means we only need to find two non-overlapping subsegments of length whose value blocks are:
Since , an brute force is absolutely fine.
For every left endpoint , extend the right endpoint . Maintain:
If a duplicate appears, we can stop extending this , because every longer segment still contains that duplicate and can never be good. Brutal, but correct.
For a segment with length
it is good iff:
When it is good, it represents the value block starting at . For each pair , store:
We only need min and max starts, not all occurrences. Nice little compression, no nonsense.
Now fix a length and a value-block start .
We need one occurrence of block and one occurrence of block .
Let their sets of starting positions be and . Segments have length , so starts and are non-overlapping iff:
There exists an occurrence from before one from iff:
Similarly, there exists one from before one from iff:
So the condition is simply:
We scan from large to small and output the first valid one.
Enumerating all good subsegments takes:
Checking all possible adjacent value blocks also takes:
Memory is:
With , this comfortably fits. The constraints are basically screaming “quadratic or bust”, and quadratic works.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
int stride = n + 2;
const unsigned short INF = 65535;
vector<unsigned short> first((n + 1) * stride, INF);
vector<unsigned short> last((n + 1) * stride, 0);
auto id = [&](int len, int mn) {
return len * stride + mn;
};
vector<int> seen(n + 1, 0);
int timer = 0;
for (int l = 1; l <= n; l++) {
++timer;
int mn = n + 1, mx = 0;
for (int r = l; r <= n; r++) {
int v = a[r];
if (seen[v] == timer) break;
seen[v] = timer;
mn = min(mn, v);
mx = max(mx, v);
int len = r - l + 1;
if (mx - mn + 1 == len) {
int pos = id(len, mn);
if (l < first[pos]) first[pos] = (unsigned short)l;
if (l > last[pos]) last[pos] = (unsigned short)l;
}
}
}
int ans = 0;
for (int len = n / 2; len >= 1 && ans == 0; len--) {
for (int x = 1; x + 2 * len - 1 <= n; x++) {
int p1 = id(len, x);
int p2 = id(len, x + len);
if (last[p1] == 0 || last[p2] == 0) continue;
int f1 = first[p1], l1 = last[p1];
int f2 = first[p2], l2 = last[p2];
if (f1 + len <= l2 || f2 + len <= l1) {
ans = len;
break;
}
}
}
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);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
int stride = n + 2;
const unsigned short INF = 65535;
vector<unsigned short> first((n + 1) * stride, INF);
vector<unsigned short> last((n + 1) * stride, 0);
auto id = [&](int len, int mn) {
return len * stride + mn;
};
vector<int> seen(n + 1, 0);
int timer = 0;
for (int l = 1; l <= n; l++) {
++timer;
int mn = n + 1, mx = 0;
for (int r = l; r <= n; r++) {
int v = a[r];
if (seen[v] == timer) break;
seen[v] = timer;
mn = min(mn, v);
mx = max(mx, v);
int len = r - l + 1;
if (mx - mn + 1 == len) {
int pos = id(len, mn);
if (l < first[pos]) first[pos] = (unsigned short)l;
if (l > last[pos]) last[pos] = (unsigned short)l;
}
}
}
int ans = 0;
for (int len = n / 2; len >= 1 && ans == 0; len--) {
for (int x = 1; x + 2 * len - 1 <= n; x++) {
int p1 = id(len, x);
int p2 = id(len, x + len);
if (last[p1] == 0 || last[p2] == 0) continue;
int f1 = first[p1], l1 = last[p1];
int f2 = first[p2], l2 = last[p2];
if (f1 + len <= l2 || f2 + len <= l1) {
ans = len;
break;
}
}
}
cout << ans << '\n';
}
return 0;
}