链接
简单的树状数组
题意
现在告诉了你一个线段上有2*n个点,告诉了你每个点从这条线段上的那个点进入,那个点出去,让你求交叉的点的对数
思路
如图所示,按照题目中给出的案例,我们发现了一共有三对交叉的点,那么怎么求呢?
我们先观察与3这个点交叉的点,分别是2和1,假设最开始我们线连接3这个点的进入点和出入点,然后顺次连2,会发现这个时候会有一个交叉对,我们发现,3和2相交满足的条件是:3的左端点位于2的左端点的左侧,3的右端点位于2的右端点的右边,(4不和任何点相交,我们就不考虑了),再来添加1这个点,当我们添加1这个点的时候会多出两个交叉点,会发现与1相交的原理是,1的起点和终点之间有3和2的有段点,那么规律就出来了,我们每次添加的时候,只需要看添加的这段区间里面有没有某个点的右端点即可。
现在我们使用树状数组来做,我先现在不管是那个点,只需要管加入的区间即可,我们按照左区间从小到大进行排序,每加入一个,我们将答案更新,加上加入的这段区间里面的右端点的数量,更新了之后,在激昂当前区间的右端点更新到树状数组上面即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n;
int tr[N];
bool vis[N];
struct node
{
int l, r;
bool operator < (const node &t) const { return l < t.l; }
}a[N];
int lowbit(int x) { return x & -x; }
void add(int x) { for (int i = x; i <= 2 * n; i += lowbit(i)) tr[i] += 1; }
int Sum(int x)
{
int sum = 0;
for (int i = x; i; i -= lowbit(i)) sum += tr[i];
return sum;
}
signed main()
{
std::ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= 2 * n; i ++)
{
int x; cin >> x;
if (!vis[x]) { a[x].l = i; vis[x] = 1; }
else a[x].r = i;
}
sort(a + 1, a + 1 + n);
int ans = 0;
for (int i = 1; i <= n; i ++)
{
ans += (Sum(a[i].r) - Sum(a[i].l - 1));
add(a[i].r);
}
cout << ans << endl;
return 0;
}