题解:AT_agc079_c [AGC079C] Sort by Queue and Reverse
不难发现,每次加入一个数后,立刻输出所有当前应该输出的数显然不劣,所以如果忽略反转操作,只考虑添加、弹出操作,那么整个长度为
为了方便,可以把队列视作双端队列,反转操作相当于切换队列的队首方向,不妨用
设
使用带权并查集维护
这个判定也是充分的,若当前需要输出
- 若
y 在x 之前被加入,则第二个条件保证了x 从被添加的一端弹出,不会被堵住。 - 若
y 在x 之后被加入,则第一个条件保证了弹出x 的一端与加入y 的一端相反,同样不会堵住。
直接做有
对 set 维护
添加
- 取
S 中的最小元素y ,若y<x ,则将b_y,a_x 合并。 - 不断取出
T 中<x 的元素y ,将b_y,a_x 合并,再把y 从T 中删除。
然后再把
这样时间复杂度就优化到了
现在考虑如何最小化反转次数。对于长为
感觉上不太好做,不妨进一步观察性质。图中的边都形如
因此可以考察同一连通块中的点在操作序列中的相邻出现
用栈维护连通块的嵌套结构,对每个连通块维护第一次、最后一次出现对应的
总体时间复杂度为
:::success[代码]
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
using ui = unsigned int;
using ull = unsigned long long;
using u128 = unsigned __int128;
using ld = long double;
using pii = pair<int, int>;
const int MAXN = 2.5e5 + 5;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chkMin(T &x, T y) { x = y < x ? y : x; }
template<typename T> void chkMax(T &x, T y) { x = x < y ? y : x; }
constexpr int lg2(ll x) { return 63 ^ __builtin_clzll(x); }
constexpr ll bitCeil(ll x) { return x == 1 ? 1ll : 1ll << lg2(x - 1) + 1; }
int tc, n, p[MAXN], pos[MAXN << 1];
struct DSU {
int fa[MAXN << 1], d[MAXN << 1];
void init(int n) {
iota(fa + 1, fa + n + 1, 1);
fill(d + 1, d + n + 1, 0);
}
int find(int x) {
if (x == fa[x]) return x;
int f = fa[x];
fa[x] = find(fa[x]);
d[x] ^= d[f];
return fa[x];
}
bool unite(int x, int y, int z) {
int fx = find(x), fy = find(y);
z ^= d[x] ^ d[y];
if (fx == fy) return !z;
fa[fx] = fy;
d[fx] = z;
return true;
}
} dsu;
struct Info {
int rt, first, lst, sum;
};
int top;
Info stk[MAXN << 1];
int solve() {
dsu.init(n * 2);
set<int> s, t;
vector<int> ord;
int cur = 1;
for (int i = 1; i <= n; ++i) {
int x = p[i];
if (!s.empty() && *s.begin() < x && !dsu.unite(*s.begin() + n, x, 0)) return -1;
if (!s.empty() && *s.rbegin() > x && !dsu.unite(x + n, x, 1)) return -1;
while (!t.empty() && *t.begin() < x) {
int y = *t.begin();
if (!dsu.unite(y + n, x, 0)) return -1;
t.erase(t.begin());
}
auto it = s.insert(x).first;
if (it != s.begin()) t.insert(x);
if (++it != s.end()) t.insert(*it);
ord.emplace_back(x);
while (!s.empty() && *s.begin() == cur) {
ord.emplace_back(cur + n);
++cur;
s.erase(s.begin());
if (!s.empty()) t.erase(*s.begin());
}
}
int ans = 0;
for (int u : ord) pos[dsu.find(u)] = u;
for (int u : ord) {
int r = dsu.find(u), w = dsu.d[u];
if (!top || stk[top].rt != r) {
stk[++top] = {r, w, w, 0};
} else {
auto &v = stk[top];
ans += v.lst ^ w ^ v.sum;
v.lst = w;
v.sum = 0;
}
if (pos[r] == u) {
int v = stk[top--].first ^ w;
if (top) stk[top].sum ^= v;
}
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> tc;
while (tc--) {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> p[i];
cout << solve() << '\n';
}
return 0;
}
:::