#535. 「LibreOJ Round #6」花火

#535. 「LibreOJ Round #6」花火

题意:给定一个序列,至多交换一次任意两个数,使得逆序对数量最小。

数据范围:n5×105

考虑什么时候不交换,只有 ai=i 时不交换。

暴力做法,枚举 ij 交换,再求逆序对数。复杂度 O(n3logn)。预计得分 10 分。

优化,可以预处理出 fi,j 表示 i 换到 j 减少的逆序对数量,同理处理出 gj,i 表示 j 往前换到 i 变化的逆序对数,答案即为 mini<j(ansfi,jgj,i+1)。复杂度 O(n2)。预计得分 40 分。

首先,我们交换的两数一定是 i<jai>aj,否则肯定不优,再观察交换两个数时变化的逆序对数量,发现为

1+2×i<k<j[aj<ak<ai]

我们要使这部分最大,后面的部分实际上就是二维数点,求左上角 (i,ai),右下角 (j,aj) 的矩形中有多少点。

对于二维数点,有两种基本方法:考虑前缀和的思想,将询问拆成四部分计算贡献;考虑扫描线,先维护一位的偏序,再用线段树维护另一维。

考虑扫描线,预处理出所有矩形,依次计算贡献,复杂度 O(nlogn+×logn)。预计得分没之前高。

我们贪心发现,假如 k<iak>ai,我们完全可以把 i 换成 k,让答案变得更优。同理 j 类似。

换言之,我们所选的 ij 一定分别是前缀最大值和后缀最小值。可以预处理出两部分,复杂度变成 O(nlogn+××logn)。在随机数据下,两者数量接近 logn,预计得分 80 分。

我们发现瓶颈在于矩形数量过多,无法全部求出所有最大值。我们考虑反过来统计贡献,对于点 (k,ak),它能够贡献到哪些矩形中。应满足以下条件:

{i<kai>akj>kaj<ak

发现在前缀最大值序列中,k 影响的是一段区间;在后缀最大值中也是一段区间。若我们将前缀最大值和后缀最小值序列分别作为 y 轴和 x 轴,那么实际上就是对一个矩形加 1 的操作。求出哪个点权值最大,此时它影响的逆序对最多。需要的操作为区间加 1,全局求最大值。同样可以用扫描线实现。复杂度 O(nlogn)。预计得分 100 分。

总结:对于一个操作,我们可以尝试表示出其影响其他值的条件。

考虑贪心的减少交换方案数,发现所选 ijaiaj 递增。

对于二维数点的形式,可以考虑用扫描线求出所有矩形的贡献;同时也可以反过来考虑,求一个点在哪些矩形内,若此时矩形的两维都是一段区间(与前面贪心联系),我们以此作为坐标轴的两维,枚举每个点,维护它能贡献哪些矩形,同样是扫描线的形式。

#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;
}
posted @   Fire_Raku  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示