#535. 「LibreOJ Round #6」花火
题意:给定一个序列,至多交换一次任意两个数,使得逆序对数量最小。
数据范围:
考虑什么时候不交换,只有
我们要使这部分最大,后面的部分实际上就是二维数点,求左上角
对于二维数点,有两种基本方法:考虑前缀和的思想,将询问拆成四部分计算贡献;考虑扫描线,先维护一位的偏序,再用线段树维护另一维。
考虑扫描线,预处理出所有矩形,依次计算贡献,复杂度
换言之,我们所选的
发现在前缀最大值序列中,
总结:对于一个操作,我们可以尝试表示出其影响其他值的条件。
考虑贪心的减少交换方案数,发现所选
对于二维数点的形式,可以考虑用扫描线求出所有矩形的贡献;同时也可以反过来考虑,求一个点在哪些矩形内,若此时矩形的两维都是一段区间(与前面贪心联系),我们以此作为坐标轴的两维,枚举每个点,维护它能贡献哪些矩形,同样是扫描线的形式。
#include <bits/stdc++.h>
typedef long long ll;
ll read() {
ll x = 0, f = 1;
char c = getchar();
while(!isdigit(c)) {
if(c == '-') f = -1;
c = getchar();
}
while(isdigit(c)) {
x = (x << 3) + (x << 1) + (c - '0');
c = getchar();
}
return x * f;
}
ll T, o, n, ans;
ll a[500010], c[500010];
ll lowbit(ll x) {return x & (-x);}
void update(ll x) {
for(ll i = x; i <= n; i += lowbit(i)) {
c[i]++;
}
}
ll query(ll x) {
ll ret = 0;
for(ll i = x; i; i -= lowbit(i)) {
ret += c[i];
}
return ret;
}
struct node {
ll l, r, y, k;
} lne[1000010];
ll tot;
bool cmp(node a, node b) {
if(a.y != b.y) return a.y < b.y;
return a.k < b.k;
}
ll lazy[500010 << 2];
struct tr {
ll max;
} t[500010 << 2];
void pushup(ll u) {t[u].max = std::max(t[u << 1].max, t[u << 1 | 1].max);}
void pushdown(ll u) {
if(!lazy[u]) return;
t[u << 1].max += lazy[u];
t[u << 1 | 1].max += lazy[u];
lazy[u << 1] += lazy[u], lazy[u << 1 | 1] += lazy[u];
lazy[u] = 0;
}
void modify(ll u, ll l, ll r, ll L, ll R, ll k) {
if(L <= l && r <= R) {
t[u].max += k;
lazy[u] += k;
return;
}
ll mid = (l + r) >> 1;
pushdown(u);
if(L <= mid) modify(u << 1, l, mid, L, R, k);
if(R > mid) modify(u << 1 | 1, mid + 1, r, L, R, k);
pushup(u);
}
bool vis[500010];
ll st1[500010], st2[500010];
ll top1, top2;
ll binary(ll x) {
ll l = 1, r = top1, ret = top1;
while(l <= r) {
ll mid = (l + r) >> 1;
if(a[st1[mid]] >= a[x]) r = mid - 1, ret = mid;
else l = mid + 1;
}
return st1[ret];
}
ll binary2(ll x) {
ll l = 1, r = top2, ret = 1;
while(l <= r) {
ll mid = (l + r) >> 1;
if(a[st2[mid]] <= a[x]) r = mid - 1, ret = mid;
else l = mid + 1;
}
return st2[ret];
}
void Solve() {
bool flg = 0;
n = read();
for(ll i = 1; i <= n; i++) {
a[i] = read();
if(a[i] != i) flg = 1;
}
if(!flg) {
std::cout << "0\n";
return;
}
for(ll i = n; i >= 1; i--) {
ans += query(a[i]);
update(a[i]);
}
for(ll i = 1; i <= n; i++) {
if(i == 1 || a[i] > a[st1[top1]]) st1[++top1] = i, vis[i] = 1;
}
for(ll i = n; i >= 1; i--) {
if(i == n || a[i] < a[st2[top2]]) st2[++top2] = i, vis[i] = 1;
}
for(ll i = 1; i <= n; i++) {
if(vis[i]) continue;
ll l = binary(i), r = binary2(i);
// std::cout << l << " " << i << " " << r << "\n";
if(l < i && i < r) {
lne[++tot] = {i + 1, r, l, 1};
lne[++tot] = {i + 1, r, i, -1};
}
}
std::sort(lne + 1, lne + tot + 1, cmp);
ll ret = 0;
for(ll i = 1; i <= tot; i++) {
modify(1, 1, n, lne[i].l, lne[i].r, lne[i].k);
if(lne[i].y != lne[i + 1].y) ret = std::max(ret, t[1].max);
}
// std::cout << ret << "\n";
std::cout << ans - 2 * ret << "\n";
}
int main() {
Solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具