1 条题解
-
0
自动搬运
来自洛谷,原作者为

Y204335
**搬运于
2025-08-24 22:33:29,当前版本为作者最后更新于2024-10-25 15:41:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
[CCO2021] Loop Town
题目大意
有一个长 的环,有 个人,他们有初始顺序 和最终顺序 ,要求给每个人规定路线,使得路线交叉次数最少。
题目分析
两人路线的交叉其实相当于交换这两人的相对位置,题目也就是问每次可以交换相邻的两人,最少多少次交换可以把初始顺序变为最终顺序。
如果这是在链上,其实就是经典的逆序对问题,但很容易就能发现逆序对在环上是没有意义的,但逆序对描述的部分性质在环上仍然成立,逆序对描述的是一种包含关系,即 ,在这种情况下显然一定需要一次交换。同样在环上,这种包含关系也一定需要一次交换,但不同的是,像 这样的情况,在链上一定要交换,而在环上则不一定。
考虑如何处理,在环上所有人都向同一方向走一定是不劣的(显然如果方向不同,则如 的情况可能产生额外的贡献),这里钦定向右。
可以处理出每条路线被其他几条路线包含,在离散化后处理每条路线,使用树状数组可以实现 的处理,但是由于在环上,终点和起点的相对位置可以改变,从而改变路线,所以要处理 次,总复杂度为 。
考虑优化,由于是整体平移,位置的相对关系是不变的,所以只有当平移后出现了 的情况,路线完全发生变化(从一路向右一圈变为直接到达,如下图),包含关系发生变化,答案才会改变。

考虑平移 后贡献的变化情况,从而能从之前的答案直接转移,设 为 从 向右走直到 的路线上点的集合(可以从 走到 )。
- 当 完全改变时,原来满足 的 将不再满足条件,而 在完全变化前一定是首尾相接的(如上图),此时只有当 满足 时(由于 互不相同,所以不会出现重叠的情况),,所以减少的贡献可以由 算出(注意此处的 可以等于 )。
- 当 完全改变时,原来不满足 的 可能满足条件, 满足条件当且仅当 ,新增贡献也就是 。

可以发现,贡献的变化只和每一时刻的 相关,考虑快速计算 ,由于 为一个 的排列(指离散化后,不难发现中间状态是无用的),所以每一次平移 后一定有一个新的 使得 ,也就是一定会有一个新的 满足 ,此时 一定加一,但由于当 时路线会完全变化,所以要减去 ,这样就能从上一时刻的 转移到当前时刻。
可以先朴素处理初始的答案,用差分处理初始时刻的 ,并把每个人挂到对应能使得 的平移次数上,之后再计算每次平移后的答案以及 ,答案取最小值即可。
初始处理为 ,之后每次平移均摊复杂度为 ,总时间复杂度为 。
注:处理初始答案时,有多种包含的情况,代码中使用了 个树状数组,相应处理的情况以注释给出;在处理初始时刻的 时,在对差分前缀和后,要把每一个点的值减一,去掉自身;在计算每次平移的答案时,第一种情况(贡献减少)中,由于出现 的时,新的满足 的 就是 ,此时就会把 算上,如果算初始 不减去自身,在此处就会算重;由于在代码中 在答案之后更新,贡献变化的第二种情况(贡献增加)中的 显然不能在这一时刻满足 ,所以要还要减去 ,并且由于这一时刻满足 的 就是 而多算上的自身也会在这里被消掉。
代码
#include <bits/stdc++.h> #define fir first #define sec second #define ll long long using namespace std; const int N = 1e6 + 10; int n, l, id[N], lsh[N], pre[N]; ll ans, res, cnt; pair<int, int> a[N]; vector<int> x[N]; struct tree { int tr[N]; tree() { memset(tr, 0, sizeof(tr)); } void upd(int x, int w) { while (x <= n) { tr[x] += w; x += x & -x; } } int quary(int x) { int res = 0; while (x) { res += tr[x]; x -= x & -x; } return res; } } tr1, tr2, tr3; // 1 A-B-B-A 2 -B-B-A A- 3 -B-A A-B- int main() { ios::sync_with_stdio(0); cin.tie(nullptr); cin >> n >> l; for (int i = 1; i <= n; i++) { cin >> a[i].fir >> a[i].sec; lsh[i] = a[i].sec; } sort(a + 1, a + n + 1); sort(lsh + 1, lsh + n + 1); for (int i = 1; i <= n; i++) { id[i] = lower_bound(lsh + 1, lsh + n + 1, a[i].sec) - lsh; } for (int i = 1; i <= n; i++) { if (id[i] < i) { tr2.upd(id[i], 1); } } for (int i = 1; i <= n; i++) { if (i <= id[i]) { pre[i]++; pre[id[i] + 1]--; x[n - (id[i] - i)].push_back(i); res += tr1.quary(n) - tr1.quary(id[i] - 1); res += tr2.quary(n) - tr2.quary(id[i] - 1); tr1.upd(id[i], 1); } else { pre[1]++; pre[id[i] + 1]--; pre[i]++; x[i - id[i]].push_back(i); res += tr3.quary(n) - tr3.quary(id[i] - 1); tr1.upd(n, 1); tr3.upd(id[i], 1); } } for (int i = 1; i <= n; i++) { pre[i] += pre[i - 1]; } for (int i = 1; i <= n; i++) { // self pre[i]--; } ans = res; for (int i = 1; i < n; i++) { //--> for (auto j : x[i]) { res += (pre[j] + i - cnt - x[i].size()) - (n - (pre[j] + i - cnt)); } cnt += x[i].size(); ans = min(ans, res); } cout << ans; return 0; }
- 1
信息
- ID
- 7164
- 时间
- 4000ms
- 内存
- 128MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者