一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
题目描述
这是codeforces的线段树专题。
D - Segment Tree, part 1 - D. Intersecting Segments
Given an array of 2n numbers, each number from 1 to n in it occurs exactly twice. We say that the segment y intersects the segment x if exactly one occurrence of the number y is between the occurrences of the number x. Find for each segment i how many segments there are that intersect with it.
Input The first line contains the number n (1≤n≤105), the second line contains 2n numbers. It is guaranteed that every number from 1 to n occurs exactly twice.
Output Print n numbers, the i-th number is equal to the number of segments that intersect with the segment i.
Example input
5
5 1 2 2 3 1 3 4 5 4
output
1 0 1 1 1
问题解析
这题意思是说,给你一个数组,这个数组长度为2n,元素都由两个1~n的数组成,每两个相同的树组成一个段,问你每个段和多少个段相交。相交意思是说,有一个的段的一个端点在你的段里,另一个端点不在,比如5和4就是相交,但5和1不是。
这里我们要进行两次计算,第一次从左往右,计算左端点在,右端点不在的情况,第二次从右往左,计算右端点在,左端点不在的情况。
用到的线段树是单点修改+区间和,一开始线段树全为0。第一次先记录下每个左端点的坐标,当遍历到左端点时,在线段树里把对应位置变成1,当遍历到右端点时,计算左端点到右端点的区间和,这个区间和就是与多少个段相交。并且计算完区间和后把左端点的值变成0。
为什么区间和可以表示与多少个段相交?因为就像我们说的,遍历到左端点才会把值从0变成1,而我们遇到右端点后又会变成0,我们左端点到右端点的区间和为多少,就说明有多少个段左端点在我们区间内,但右端点不在(如果在那左端点也该是0)。正好是相交的规定。但这个方法只能算左端点到在段内的情况,所以我们还要从右往左遍历一趟,计算右端点在左端点不在的情况。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 300050;
int f[4 * N], a[N], n,b[N];
void revise(int k, int l, int r, int x,int y)
{
if (l == r)
{
f[k] += y;
return;
}
int m = (l + r) / 2;
if (x <= m)revise(k + k, l, m, x, y);
else
revise(k + k + 1, m + 1, r, x, y);
f[k] = f[k + k] + f[k + k + 1];
}
int calc(int k, int l, int r, int x, int y)
{
if (l == x && y == r)
{
return f[k];
}
int m = (l + r) / 2;
if (y <= m)return calc(k + k, l, m, x, y);
else
if (x > m)return calc(k + k + 1, m + 1, r, x, y);
else return calc(k + k , l, m, x, m) + calc(k + k+1, m + 1, r, m + 1, y);
}
int main()
{
unordered_map<int, int>mymap;
map<int, int>cnt;
cin >> n;
vector<int>v(n);
for (int i = 1; i <= 2*n; i++)
{
cin >> a[i];
if (mymap[a[i]] == 0)mymap[a[i]] = i;
}
for (int i = 1; i <= 2*n; i++)
{
if (cnt[a[i]] == 0)
{
cnt[a[i]] = 1;
revise(1, 1, 2 * n, mymap[a[i]], 1);
}
else
{
revise(1, 1, 2 * n, mymap[a[i]], -1);
v[a[i]-1] = calc(1, 1, 2*n, mymap[a[i]], i);
}
}
int ans = n;
int l = 1, r = 2 * n;
while (l < r)
{
swap(a[l], a[r]);
l++, r--;
}
memset(f, 0, sizeof f);
mymap.clear();
cnt.clear();
for (int i = 1; i <= 2 * n; i++)
{
if (mymap[a[i]] == 0)mymap[a[i]] = i;
}
for (int i = 1; i <= 2 * n; i++)
{
if (cnt[a[i]] == 0)
{
cnt[a[i]] = 1;
revise(1, 1, 2 * n, mymap[a[i]], 1);
}
else
{
revise(1, 1, 2 * n, mymap[a[i]], -1);
v[a[i] - 1] += calc(1, 1, 2 * n, mymap[a[i]], i);
}
}
for (auto i : v)cout << i << " ";
return 0;
}