Inversion Graph
题面翻译
现在有一个序列 $p_1,p_2,\dots,p_n$,你需要构建一个无向图,规则是:当 $i < j$ 且 $p_i > p_j$ 时在 $i$ 和 $j$ 之间连一条无向边。问最后图中会有几个连通块。
Translate by CmsMartin
题目描述
You are given a permutation $ p_1, p_2, \dots, p_n $ . Then, an undirected graph is constructed in the following way: add an edge between vertices $ i $ , $ j $ such that $ i < j $ if and only if $ p_i > p_j $ . Your task is to count the number of connected components in this graph.
Two vertices $ u $ and $ v $ belong to the same connected component if and only if there is at least one path along edges connecting $ u $ and $ v $ .
A permutation is an array consisting of $ n $ distinct integers from $ 1 $ to $ n $ in arbitrary order. For example, $ [2,3,1,5,4] $ is a permutation, but $ [1,2,2] $ is not a permutation ( $ 2 $ appears twice in the array) and $ [1,3,4] $ is also not a permutation ( $ n=3 $ but there is $ 4 $ in the array).
输入格式
Each test contains multiple test cases. The first line contains a single integer $ t $ ( $ 1 \le t \le 10^5 $ ) — the number of test cases. Description of the test cases follows.
The first line of each test case contains a single integer $ n $ ( $ 1 \le n \le 10^5 $ ) — the length of the permutation.
The second line of each test case contains $ n $ integers $ p_1, p_2, \dots, p_n $ ( $ 1 \le p_i \le n $ ) — the elements of the permutation.
It is guaranteed that the sum of $ n $ over all test cases does not exceed $ 2 \cdot 10^5 $ .
输出格式
For each test case, print one integer $ k $ — the number of connected components.
样例 #1
样例输入 #1
6
3
1 2 3
5
2 1 4 3 5
6
6 1 4 2 5 3
1
1
6
3 2 1 6 5 4
5
3 1 5 2 4
样例输出 #1
3
3
1
1
2
1
提示
Each separate test case is depicted in the image below. The colored squares represent the elements of the permutation. For one permutation, each color represents some connected component. The number of distinct colors is the answer.
解法一
可以想到一个独立的块的元素是连续的,比如654,这一个连续的块的最小值是4,如果在6前面有比4大的元素,那就一定会加到这一个块里面,如果没有就会构成一个新的块了,比如————321654,写法是求前缀和,如果前缀和的值等于i*(1+i)/2,那么就形成一个新的块
const int N = 200010;
int a[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
a[i] = a[i - 1] + x;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
if (a[i] == i * (i + 1) / 2) ans++;
}
cout << ans << '\n';
}
解法二
模拟,从后往前,首先找一段从前往后是降序的段,段左边是他的最大值,右边是最小值,
对于在后面已经形成的连通块,把他们的最小值拿出来,用当前段的最大值mx和这些最小值mi比较,如果mx大于某些mi,那么当前段就可以和他们合并,合并之后的最小值用这些mi和当前段的最小值比较,取一个最小
const int N = 200010;
int a[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int i = n;
priority_queue<int, vector<int>, greater<int> >xg;
while (i >= 1)
{
int j = i-1;
while (j >= 1 && a[j] > a[i] && a[j] < a[j - 1]) j--;
j++;
if (!xg.size()) xg.push(a[i]);
else
{
int mmin = 1e9;
while (xg.size() && xg.top() < a[j])
{
mmin = min(mmin, xg.top());
xg.pop();
}
xg.push(min(a[i], mmin));
}
i = j - 1;
}
cout << xg.size() << '\n';
}