一、内容
题意:给定一个连续的无限序列{1,2,3,4,5,6…},然后给出几个区间,将2端的值进行swap,最后求有多少个逆序对。
二、思路
-
将题目要进行交换的点都保存下来,然后在把中间没有出现的区间离散化成一个点,权值是这段区间有几个数
例如 1 2 5 9 就可以化成 1 2 3 4 5 6 . 3代表【3,4】权值为2,5代表【6,7,8】权值为3。 -
然后用一个vecotr保存这些离散化后的点,vector里面存一个pair,pair的first代表这个点的值是多少,second代表有多少个数,5所代表的【6,7,8】 所以second保存的是3,first保存的是6。
-
最后模拟一下端点交换,用一个id数组存储离散化后的各个数在树状数组中的序号,然后二分在vector中查找位置进行交换,最后id数组保存的就是交换后的位置,然后逆序求一下逆序对就可以了。
三、代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define X first
#define Y second
#define P pair<int ,int>
using namespace std;
const int maxn = 1e5 + 5;
struct {
int l, r;
}itval[maxn];
vector<P> pa;//第一个值保存这个区间代表的点 第二个值保存权值(就是有几个数)
int w[maxn << 2];//保存每个点代表有多少个数
int id[maxn << 2]; //保存交换后的数在树状数组上的id
int c[maxn << 2]; //树状数组
int n, cnt;
void update(int x, int v) {
for (int i = x; i <= cnt; i += i & (-i)) {
c[i] += v;
}
}
long long query(int x) {
long long ans = 0;
for (int i = x; i > 0; i -= i & (-i)) {
ans += c[i];
}
return ans;
}
void swap(int &a, int &b) {
int tem = a;
a = b;
b = tem;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &itval[i].l, &itval[i].r);
pa.push_back(P(itval[i].l, 1));
pa.push_back(P(itval[i].r, 1)) ;
}
//离散化
sort(pa.begin(), pa.end());
//去重
pa.erase(unique(pa.begin(), pa.end()), pa.end());
cnt = pa.size();
//将中间的区间化成一个点 保存这段区间有多少个数
for (int i = 1; i < cnt; i++) {
if (pa[i].X > pa[i - 1].X + 1) { //这个数组用来二分查找进行位置的查询
int x = pa[i - 1].X + 1;
int y = pa[i].X - pa[i - 1].X - 1;
pa.push_back(P(x, y));
}
}
//再次离散化
sort(pa.begin(), pa.end());
cnt = pa.size();
//用w,num来进行模拟交换
for (int i = 0; i < cnt; i++) {
w[i + 1] = pa[i].Y;
id[i + 1] = i + 1;
}
//模拟交换
for (int i = 1; i <= n; i++) {
int xx = lower_bound(pa.begin(), pa.end(), P(itval[i].l,0)) - pa.begin() + 1; //必须加1才是第几个
int yy = lower_bound(pa.begin(), pa.end(), P(itval[i].r,0)) - pa.begin() + 1;
swap(id[xx], id[yy]);
}
long long ans = 0;
//求逆序对
for (int i = cnt; i > 0; i--) {
ans += query(id[i]) * w[id[i]];
update(id[i], w[id[i]]);
}
printf("%I64d\n\n", ans);
return 0;
}