Перестановка∗ p длины n записана по часовой стр...

बनाया गया: 12 नवंबर 2026

का उपयोग करके उत्तर दिया गया GPT-6 द्वारा Chat01

प्रश्न

Перестановка∗
p
длины n
записана по часовой стрелке на круговой шкале.

Выберите начальную позицию s
(1≤s≤n
) и прочитайте один полный круг по часовой стрелке, начиная с ps
и переходя после pn
к началу. Получившаяся последовательность имеет вид

q=[ps,ps+1,…,pn,p1,…,ps−1],

и тоже имеет длину n
.

Для любого непустого префикса этой последовательности [q1,q2,…,qk]
(k≥1
) пусть S
— соответствующее множество значений, то есть S={q1,q2,…,qk}
. Разобьём S
на максимальные отрезки последовательных целых чисел, и назовём эти отрезки блоками множества S
.

Например, у множества S={1,2,5,7,8,9}
есть 3
блока: {1,2}
, {5}
и {7,8,9}
.

Начальная позиция s
называется хорошей тогда и только тогда, когда для каждого непустого префикса q
соответствующее множество S
имеет не более 2
блоков.

Найдите количество хороших начальных позиций.

∗
Перестановкой длины n
является массив, состоящий из n
различных целых чисел от 1
до n
в произвольном порядке. Например, [2,3,1,5,4]
— перестановка, но [1,2,2]
не перестановка (2
встречается в массиве дважды) и [1,3,4]
тоже не перестановка (n=3
, но в массиве встречается 4
).

Входные данные
Каждый тест состоит из нескольких наборов входных данных. В первой строке находится одно целое число t
(1≤t≤104
) — количество наборов входных данных. Далее следует описание наборов входных данных.

Первая строка каждого набора входных данных содержит одно целое число n
(1≤n≤2⋅105
) — длину p
.

Вторая строка каждого набора входных данных содержит n
целых чисел p1,p2,…,pn
(1≤pi≤n
, все pi
различны) — элементы p
.

Гарантируется, что сумма значений n
по всем наборам входных данных не превосходит 2⋅105
.

Выходные данные
Для каждого набора входных данных выведите одно целое число — количество хороших начальных позиций.

реши на с++ с объяснением своей идеи

विचार

Designing the algorithm

I’m modeling each window’s component count as selected values minus adjacent selected pairs. A straightforward sliding window may fail because component counts can decrease, so I’m exploring range updates over circular starts as elements are added.

Refining range updates

I’m using each new value’s neighboring occurrences to apply step-function updates across possible starts. A segment tree can track component counts, discard starts reaching three components, and handle circular windows in roughly O(nlog⁡n)O(n\log n) time.

Worked for 16s

उत्तर

Text version

Идея решения — Segment Tree + Lazy Propagation

Решим задачу за O(nlog⁡n)O(n\log n) на один тест.

Главная идея: будем рассматривать все возможные начальные позиции одновременно и поддерживать количество блоков для каждой из них.

1. Как изменяется количество блоков?

Пусть у нас есть множество SS, и мы добавляем в него новое число xx.

Возможны четыре ситуации:

УсловиеИзменение числа блоков
Нет ни x−1x-1, ни x+1x+1+1+1
Есть только x−1x-100
Есть только x+1x+100
Есть оба соседа−1-1

Таким образом,

Δ=1−[x−1∈S]−[x+1∈S]\Delta=1-[x-1\in S]-[x+1\in S]

Здесь квадратные скобки равны 1, если условие истинно, и 0 иначе.

Это ключевое наблюдение: при добавлении одного числа количество блоков изменяется только на −1-1, 00 или +1+1.

2. Рассмотрим перестановку как удвоенный массив

Чтобы удобно работать с циклическими сдвигами, мысленно допишем перестановку к самой себе:

a=[p1,…,pn,p1,…,pn]a=[p_1,\ldots,p_n,p_1,\ldots,p_n]

Каждая начальная позиция ss соответствует подмассиву длины nn:

as,as+1,…,as+n−1a_s,a_{s+1},\ldots,a_{s+n-1}

Будем двигать правую границу rr слева направо.

Для каждой начальной позиции ss хотим поддерживать количество блоков в множестве

Ss,r={as,as+1,…,ar}.S_{s,r}=\{a_s,a_{s+1},\ldots,a_r\}.

Если в какой-то момент число блоков стало хотя бы 3, позиция ss навсегда становится плохой.

Важно: число блоков может уменьшаться, поэтому обычные два указателя здесь не подходят.

3. Как обновлять сразу много начальных позиций?

Пусть сейчас добавляем x=arx=a_r.

Будем хранить last[v] — последнюю позицию числа vv перед текущей позицией rr.

Для начальной позиции ss число x−1x-1 уже находится в рассматриваемом подмассиве тогда и только тогда, когда

s≤last[x−1].s\le last[x-1].

Аналогично для x+1x+1.

Поэтому изменение количества блоков можно выполнить тремя операциями:

  1. Прибавить 11 всем началам s≤rs\le r.
  2. Вычесть 11 для всех s≤last[x−1]s\le last[x-1], если x>1x>1.
  3. Вычесть 11 для всех s≤last[x+1]s\le last[x+1], если x<nx<n.

Все эти операции — прибавление числа на отрезке, что удобно делать деревом отрезков с lazy propagation.

В дереве будем хранить максимальное количество блоков среди рассматриваемых начал.

Если для некоторого начала количество блоков достигло 3, найдём такую позицию и исключим её навсегда, установив значение −INF-INF.

Каждая позиция исключается не более одного раза.


Решение на C++17

cpp
#include <bits/stdc++.h> using namespace std; const int NEG = -1000000000; struct SegTree { int n; vector<int> mx, lazy; SegTree(int n) : n(n) { mx.assign(4 * n + 5, 0); lazy.assign(4 * n + 5, 0); } void apply(int v, int val) { mx[v] += val; lazy[v] += val; } void push(int v) { if (lazy[v] != 0) { apply(v * 2, lazy[v]); apply(v * 2 + 1, lazy[v]); lazy[v] = 0; } } void pull(int v) { mx[v] = max(mx[v * 2], mx[v * 2 + 1]); } void add(int v, int tl, int tr, int l, int r, int val) { if (l > r) return; if (l == tl && r == tr) { apply(v, val); return; } push(v); int tm = (tl + tr) / 2; add(v * 2, tl, tm, l, min(r, tm), val); add(v * 2 + 1, tm + 1, tr, max(l, tm + 1), r, val); pull(v); } void add(int l, int r, int val) { if (l <= r) add(1, 1, n, l, r, val); } // Найти позицию со значением >= 3 // внутри отрезка [l, r]. int find_bad(int v, int tl, int tr, int l, int r) { if (l > r || mx[v] < 3) return -1; if (tl == tr) return tl; push(v); int tm = (tl + tr) / 2; int res = find_bad( v * 2, tl, tm, l, min(r, tm) ); if (res == -1) { res = find_bad( v * 2 + 1, tm + 1, tr, max(l, tm + 1), r ); } return res; } int find_bad(int l, int r) { if (l > r) return -1; return find_bad(1, 1, n, l, r); } void erase(int v, int tl, int tr, int pos) { if (tl == tr) { mx[v] = NEG; lazy[v] = 0; return; } push(v); int tm = (tl + tr) / 2; if (pos <= tm) erase(v * 2, tl, tm, pos); else erase(v * 2 + 1, tm + 1, tr, pos); pull(v); } void erase(int pos) { erase(1, 1, n, pos); } }; void solve() { int n; cin >> n; vector<int> p(n + 1); for (int i = 1; i <= n; i++) cin >> p[i]; SegTree st(n); vector<int> last(n + 2, 0); int ans = n; for (int r = 1; r <= 2 * n - 1; r++) { int x = p[(r - 1) % n + 1]; int R = min(r, n); // Добавляем новый элемент x st.add(1, R, 1); // Если x-1 уже присутствует, // новый блок не создаётся if (x > 1) { st.add(1, min(last[x - 1], n), -1); } // Если x+1 уже присутствует, // блоки могут объединиться if (x < n) { st.add(1, min(last[x + 1], n), -1); } last[x] = r; // Только подмассивы длины <= n int L = max(1, r - n + 1); // Удаляем все плохие начала while (true) { int pos = st.find_bad(L, R); if (pos == -1) break; st.erase(pos); ans--; } } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }

Пример работы

Рассмотрим перестановку:

p=[1,3,5,2,4]p=[1,3,5,2,4]

Для начальной позиции s=1s=1 получаем:

q=[1,3,5,2,4]q=[1,3,5,2,4]

Добавление элементов Шаг из 5

Количество блоков

[Назад] [Далее]

После добавления числа 55 имеем:

इस प्रश्नोत्तर को साझा करें