CF1672I PermutationForces
一个观察是这个题可以直接贪心选最小。证明考虑删除一个位置后,如果一个 \(|i-p_i|\) 变大,那么新的 \(|i-p_i|\) 也不会超过刚刚操作的代价。
考虑把排列看成上下各 \(1\sim n\) 的二分图,上面的第 \(i\) 个点向下面 \(p_i\) 连边。操作不会改变相对位置,所以考虑一直对于整个图操作,对于每组匹配维护当前代价 \(v_i\)。每次操作实际上就是把与选中线段相交的匹配的 \(v-1\)。
称 \(i\le p_i\) 的为 \(1\) 类匹配,\(i>p_i\) 为 \(2\) 类匹配,令它们代表的区间就是 \([\min(i,p_i),\max(i,p_i)]\)。
一个观察是,对于两组同类匹配的区间 \([x_1,y_1], [x_2,y_2]\),如果 \(x_1<x_2<y_2<y_1\),那么我们肯定是先操作 \([x_2,y_2]\) 再操作 \([x_1,y_1]\)。
同时我们可以注意到,如果我们只保留那些不存在 \([x_2,y_2]\) 的区间 \([x_1,y_1]\),那么整个图也是很有规律的。具体地,两类线段分别不相交,而且对于每一类中的线段,另一类中和它有交的线段形成一个区间,而这是很好做的。
考虑动态维护这些极小的区间,每次我们删除了一个区间后就尝试再加入同类的区间,在加入一个区间时再计算之前的删除的贡献。
注意算这些贡献时可能出现动态二维偏序的形式,但是我们都可以做一些处理。
考虑同类贡献时,因为这道题过程的特殊性保证了不会有包含当前区间的区间被删除,所以不难动态维护。
考虑异类贡献时,我们可以直接从刚刚被删的区间继承,然后新增的部分是只和 \(l\) 或 \(r\) 有关的式子,然后就可以维护了。
总复杂度 \(O(n\log n)\)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int> ;
template <typename T> void Chkmin(T &x, T y) { x = min(x, y); }
template <typename T> void Chkmax(T &x, T y) { x = max(x, y); }
const int inf = 1e9;
const int kN = 5e5 + 5, kS = kN * 4;
int n;
int p[kN];
int len[2];
pii seg[2][kN];
set<pii> stl[2], str[2];
bool used[2][kN], del[2][kN];
struct BIT {
int tr[kN];
void Update(int x, int v) {
for(; x <= n; x += (x & -x)) tr[x] += v;
}
int Query(int x) {
int ans = 0;
for(; x; x &= (x - 1)) ans += tr[x];
return ans;
}
void Set(int x, int v) {
int d = v - Query(x);
Update(x, d);
Update(x + 1, -d);
}
};
BIT bitl[2], bitr[2], bito[2];
#define ls (o << 1)
#define rs (o << 1 | 1)
struct SGT1 {
pii mn[kS];
SGT1() { fill(mn, mn + kS, pii {inf, inf}); }
void Up(int o) { mn[o] = min(mn[ls], mn[rs]); }
void Modify(int o, int l, int r, int x, pii v) {
if(l == r) return void(mn[o] = v);
int mid = (l + r) >> 1;
if(mid < x) Modify(rs, mid + 1, r, x, v);
else Modify(ls, l, mid, x, v);
Up(o);
}
pii Query(int o, int l, int r, int x, int y) {
if((l > y) || (r < x)) return pii {inf, inf};
if((l >= x) && (r <= y)) return mn[o];
int mid = (l + r) >> 1;
return min(Query(ls, l, mid, x, y), Query(rs, mid + 1, r, x, y));
}
};
SGT1 sgt1[2];
struct SGT2 {
pii mn[kS];
int tag[kS];
SGT2() { fill(mn, mn + kS, pii {inf, inf}); }
void Up(int o) { mn[o] = min(mn[ls], mn[rs]); }
void Adt(int o, int t) { mn[o].first += t, tag[o] += t; }
void Dn(int o) { if(int &t = tag[o]) Adt(ls, t), Adt(rs, t), t = 0; }
void Update(int o, int l, int r, int x, int y, int v) {
if((l > y) || (r < x)) return ;
if((l >= x) && (r <= y)) return Adt(o, v);
Dn(o);
int mid = (l + r) >> 1;
Update(ls, l, mid, x, y, v);
Update(rs, mid + 1, r, x, y, v);
Up(o);
}
void Modify(int o, int l, int r, int x, pii v) {
if(l == r) return void(mn[o] = v);
Dn(o);
int mid = (l + r) >> 1;
if(mid < x) Modify(rs, mid + 1, r, x, v);
else Modify(ls, l, mid, x, v);
Up(o);
}
};
SGT2 sgt2[2];
void AddSeg(int typ, int id, int from) {
int cl, cr, fl, fr;
tie(cl, cr) = seg[typ][id];
tie(fl, fr) = seg[typ][from];
sgt1[typ].Modify(1, 1, n, cl, pii {inf, inf});
stl[typ].emplace(cl, id);
str[typ].emplace(cr, id);
used[typ][id] = 1;
int v = cr - cl;
v -= bitl[typ].Query(n) - bitl[typ].Query(cl);
v += bitr[typ].Query(n) - bitr[typ].Query(cr);
if(from) {
int op = bito[typ].Query(from);
int rvt = 1 - typ;
op += bitr[rvt].Query(fl) - bitr[rvt].Query(cl);
op += bitl[rvt].Query(cr) - bitl[rvt].Query(fr);
bito[typ].Set(id, op);
v -= op;
}
sgt2[typ].Modify(1, 1, n, id, pii {v, id});
}
void TryAdd(int typ, int L, int R, int from) {
int lst = L - 1;
while(lst < R) {
int r, id;
tie(r, id) = sgt1[typ].Query(1, 1, n, lst + 1, R);
if(r > R) return ;
lst = seg[typ][id].first;
AddSeg(typ, id, from);
}
}
tuple<int, int, int> GetMin() {
int tp, id, mn = inf;
for(int typ : {0, 1}) {
int v, p;
tie(v, p) = sgt2[typ].mn[1];
if(mn > v) mn = v, tp = typ, id = p;
}
return make_tuple(tp, id, mn);
}
void DelSeg(int typ, int id) {
int cl, cr;
tie(cl, cr) = seg[typ][id];
sgt2[typ].Modify(1, 1, n, id, pii {inf, inf});
stl[typ].erase(pii {cl, id});
str[typ].erase(pii {cr, id});
del[typ][id] = 1;
bitl[typ].Update(cl, 1);
bitr[typ].Update(cr, 1);
int rvt = 1 - typ;
auto itl = stl[rvt].lower_bound(pii {cr, -1});
auto itr = str[rvt].lower_bound(pii {cl, -1});
if((itl == stl[rvt].begin()) || (itr == str[rvt].end())) return ;
int idl = itr -> second;
int idr = prev(itl) -> second;
bito[rvt].Update(idl, 1);
bito[rvt].Update(idr + 1, -1);
sgt2[rvt].Update(1, 1, n, idl, idr, -1);
}
int main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> p[i];
int l = min(i, p[i]), r = max(i, p[i]);
bool typ = (i > p[i]);
len[typ]++;
seg[typ][len[typ]] = pii {l, r};
}
for(int typ : {0, 1}) {
sort(seg[typ] + 1, seg[typ] + len[typ] + 1,
[&](pii x, pii y) { return x.second < y.second; });
for(int i = 1; i <= len[typ]; i++) {
int l, r;
tie(l, r) = seg[typ][i];
sgt1[typ].Modify(1, 1, n, l, pii {r, i});
}
}
TryAdd(0, 1, n, 0);
TryAdd(1, 1, n, 0);
int ans = 0;
for(int t = 1; t <= n; t++) {
int typ, id, cost;
tie(typ, id, cost) = GetMin();
Chkmax(ans, cost);
DelSeg(typ, id);
int liml = 1, limr = n;
auto it = stl[typ].lower_bound(pii {seg[typ][id].first, -1});
if(it != stl[typ].end()) limr = seg[typ][it -> second].second;
if(it != stl[typ].begin()) liml = seg[typ][prev(it) -> second].first;
TryAdd(typ, liml, limr, id);
}
cout << ans << "\n";
return 0;
}
浙公网安备 33010602011771号