P7833 [CCO2021] Loop Town题解
[CCO2021] Loop Town
题目大意
有一个长
题目分析
两人路线的交叉其实相当于交换这两人的相对位置,题目也就是问每次可以交换相邻的两人,最少多少次交换可以把初始顺序变为最终顺序。
如果这是在链上,其实就是经典的逆序对问题,但很容易就能发现逆序对在环上是没有意义的,但逆序对描述的部分性质在环上仍然成立,逆序对描述的是一种包含关系,即
考虑如何处理,在环上所有人都向同一方向走一定是不劣的(显然如果方向不同,则如
可以处理出每条路线被其他几条路线包含,在离散化后处理每条路线,使用树状数组可以实现
考虑优化,由于是整体平移,位置的相对关系是不变的,所以只有当平移后出现了
考虑平移
- 当
path_i 完全改变时,原来满足path_j\subset path_i 的j 将不再满足条件,而path_i 在完全变化前一定是首尾相接的(如上图),此时只有当j 满足a_i\in path_j 时(由于a_i 互不相同,所以不会出现重叠的情况),path_j\not\subset path_i ,所以减少的贡献可以由n-sum_{a_i\in path_j} 算出(注意此处的j 可以等于i )。 - 当
path_i 完全改变时,原来不满足path_i\subset path_j 的j 可能满足条件,j 满足条件当且仅当a_i\in path_j ,新增贡献也就是sum_{a_i\in path_j} 。
可以发现,贡献的变化只和每一时刻的
可以先朴素处理初始的答案,用差分处理初始时刻的
初始处理为
注:处理初始答案时,有多种包含的情况,代码中使用了
代码
#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;
}