P3660 [USACO17FEB]Why Did the Cow Cross the Road III G


题面

给定长度为 \(2 \times N\) 的序列,\(1\sim N\) 各处现过 \(2\) 次,\(i\) 第一次出现位置记为 \(a_i\),第二次记为 \(b_i\),求满足 \(a_i 的对数

思路

1.暴力

没什么好讲的。时间复杂度为 \(O(n^2)\)。不开O2可以拿 \(40\) 分。开O2可以拿 \(50\) 分。

#include 
using namespace std;

int n;
vector arr[100005];
int cnt;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= 2 * n; i++) {
		int k;
		cin >> k;
		arr[k].push_back(i);
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if ((arr[i][0] < arr[j][0]) && (arr[j][0] < arr[i][1]) && (arr[i][1] < arr[j][1])) {
				cnt++;
			}
		}
	}
	cout << cnt << endl;
	return 0;
}

2.树状数组(正解)

我们可以发现,如果出现题设的情况,那么一定他的左端点在前面那一种数字的右面并且他的右端点也在前面那一种数字的右面。(废话)。

然后按左端点排序。将右节点插入树状数组,然后查询左右区间和就好了。

至于这个过程,如果还是暴力就还是 \(O(n^2)\)。可以使用树状数组/线段树。(这里使用树状数组)。可以做到 \(O(n\log n)\) 的复杂度。

代码如下:

#include 
using namespace std;

int tree[100010<<2];
int n,answer;

struct Node{
	int l,r;
	bool operator<(Node nth){
		return l0;i-=lowbit(i)){
		result+=tree[i];
	}
	return result;
}

int ranged_query(int l,int r){
	return query(r)-query(l-1);
}

int main(){
	cin>>n;
	for(int i=1;i<=n*2;i++){
		int k;
		cin>>k;
		if(!nsta[k]){
			a[k].l=i;
			nsta[k]=1;
		}
		else{
			a[k].r=i;
		}
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		answer+=ranged_query(a[i].l,a[i].r);
		update(a[i].r,1);
	}
	cout<