洛谷P1996 火柴排队 题解

题目描述

输入 na,b 两个长度为 n 的序列。a,b 序列相邻两项可以互相交换。
求使 Σin(aibi)2 为最小值的最小交换次数,答案对 1e83 取模。

思路

Σin(aibi)2 化简后即求 Σ(ai×bj) 的最小值

核心

排序不等式:对两个相同长度的序列,求上述值的最小值,有一个重要结论:

  • 同序乘 ≥ 乱序乘 ≥ 逆序乘

证明:

  • 两序列按从小到大排序
  • 求证,aibi+ai+1bi+1aibi+1+ai+1bi
  • 移项得证 bi+1(ai+1ai)bi(ai+1ai)
  • n 开始不断调整,即得证同序乘目标值最小

流程

  1. 离散化高度
  2. 因为要同序乘,且序列中各值各不相同,则代表离散化后 a,b 两个序列可以一一映射。
    所以设 mp[b[i]] = i ,那么要让 a 中序列调整到对应位置就执行 a[i]=mp[a[i]]
  3. 可以观察到,要让现在的 a 升序排列,交换次数其实就是归并排序中求逆序对个数,两者互相等价
  4. 最后套用一下树状数组求逆序对的模板即可(值域树状数组)。

C++ Code

#include<bits/stdc++.h> #define pb push_back using namespace std; const int N = 1e5 + 10, p = 1e8 - 3; int tr[N], a[N], b[N], n, mp[N]; vector<int> va, vb; int lowbit(int x){ return x & -x; } void add(int x, int c){ // 单点修改 for(int i = x; i <= n; i += lowbit(i)) tr[i] += c; } int ask(int x){ // 查询区间 [1, x] int res = 0; for(int i = x; i; i -= lowbit(i)) res += tr[i]; return res; } int get(int x, int t){ if(!t) return lower_bound(va.begin(), va.end(), x) - va.begin() ; else return lower_bound(vb.begin(), vb.end(), x) - vb.begin() ; } int main(){ cin >> n; for(int i = 1; i <= n; i++){ cin >> a[i]; va.pb(a[i]); } for(int i = 1; i <= n; i++){ cin >> b[i]; vb.pb(b[i]); } sort(va.begin(), va.end()); va.erase(unique(va.begin(), va.end()), va.end()); sort(vb.begin(), vb.end()); vb.erase(unique(vb.begin(), vb.end()), vb.end()); for(int i = 1; i <= n; i++){ a[i] = get(a[i], 0); b[i] = get(b[i], 1); mp[b[i]] = i; } for(int i = 1; i <= n; i++) a[i] = mp[a[i]]; long long res = 0; for(int i = 1; i <= n; i++){ res = (res + ask(n) - ask(a[i])) % p; add(a[i], 1); } cout << res << endl; return 0; } `

__EOF__

本文作者Roshin
本文链接https://www.cnblogs.com/Roshin/p/solution_2.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Roshin  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
-->
点击右上角即可分享
微信分享提示