「Codeforces」C. Inversion Graph

C. Inversion Graph

https://codeforces.com/contest/1638/problem/C

题目描述

给你一个序列,找到所有逆序对 ( a i , a j ) (a_i, a_j) (ai,aj) 并将它们连接起来,求连通块个数。

输入描述

每个测试包含多个测试用例。第一行包含一个整数 t (1≤t≤105)——测试用例的数量。测试用例的描述如下。

每个测试用例的第一行包含一个整数 n (1≤n≤105) — 排列的长度。

每个测试用例的第二行包含 n 个整数 p1,p2,…,pn (1≤pi≤n) — 排列的元素。

保证所有测试用例的 n 之和不超过 2×105

输出描述

连通块个数

样例

#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
3
3
1
1
2
1

每个单独的测试用例如下图所示。彩色方块代表排列的元素。对于一个排列,每种颜色代表一些连通分量。不同颜色的数量就是答案。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u6Wdrd70-1683084240478)(images/1645000749170.png)]

提示

解析

观察上面样例的图,可以得知如下信息:

  1. 连通块的最大值一定大于右边的数(根据逆序对的特性可知)。
  2. 若某个数属于某个连通块,那么这个数一定小于该连通块的最大值。
  3. 若某个数大于左边的所有数(这也意味着左边的数无法与该数相连),则该数是一个新的连通块。

有上面的几个信息,我们知道只需要维护最大值就可以了,简单来说就是“大于该数的就是新的连通块,小于该数的则连接该连通块”,因此我们只需要维护最大值就行,如何维护?

我们可以使用单调栈维护(自顶向下为从大到小),只需要存储最大值就行,最后栈内剩余的数量即为连通块的数量。


[ 3 , 1 , 5 , 2 , 4 ] [3, 1, 5, 2, 4] [3,1,5,2,4] 为例: [ 3 ] → [ 3 ] → [ 3 , 5 ] → [ 5 ] → [ 5 ] [3] \rightarrow [3] \rightarrow [3, 5] \rightarrow [5] \rightarrow [5] [3][3][3,5][5][5]

  • 栈为空,入栈 3,得 [ 3 ] [3] [3]

  • [ 3 ] [3] [3] ,因为 1 < 3 1 < 3 1<3 ,所以 1 是属于 3 的连通块。

  • [ 3 ] [3] [3] ,因为 5 > 3 5 > 3 5>3 ,所以 5 是新的连通块,得到 [ 3 , 5 ] [3, 5] [3,5]

  • [ 3 , 5 ] [3, 5] [3,5] ,因为 2 < 5 2 < 5 2<5 2 < 3 2 < 3 2<3 ,表示 2 即使属于 5 的连通块,也是属于 3 的连通块,所以 3 也属于 5 的连通块。

    这也是维护时需要注意的点:判断一个数是不是属于当前连通块的时候,如果是,则还需要判断是否也属于前面的连通块。


单调栈是否一定保证为单调递增(自底向上为从小到大)?

答:一定,因为若数小于栈顶即不属于当前连通块,就不入栈,若大于栈顶,则是新的连通块,会入栈。

AC Code

public class Main {
    
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));

    public static void main(String[] args) throws Exception {
    
    
        int T = nextInt();
        while(T-- != 0) {
    
    
            int n = nextInt();
            Stack<Integer> stack = new Stack<>();
            for(int i = 0; i < n; i++) {
    
    
                int x = nextInt();
                if(stack.isEmpty()) {
    
    
                    stack.push(x);
                    continue;
                }
                if(x < stack.peek()) {
    
     // 数小于栈顶,意为属于该连通块
                    int top = stack.peek(); // 保留最大连通块的值
                    // 判断是否也属于前面的连通块
                    while(!stack.isEmpty() && stack.peek() > x) stack.pop();
                    stack.push(top);
                } else {
    
    
                    stack.push(x); // 这是新的连通块
                }
            }
            out.println(stack.size());
        }
        out.flush();
    }

    public static int nextInt() throws Exception {
    
    
        st.nextToken();
        return (int) st.nval;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43098197/article/details/130471245