解题思路

这道题的关键点在于:

  1. 个点形成一条链,边的编号从
  2. 对于第 条边,其权值 定义为:在第 条边左侧 且点权为 的点数,与在第 条边右侧 且点权为 的点数的乘积之和
  3. 需要计算每条边的权值

解题思路:

  1. 使用两个哈希表分别记录左侧和右侧的点权计数
  2. 初始时所有点都在右侧
  3. 从左到右遍历每个位置,计算当前边的权值:
    • 对于每个不同的点权值,贡献为左侧该值的个数 × 右侧该值的个数
    • 移动当前点到左侧,更新计数

代码

#include <iostream>
#include <unordered_map>
using namespace std;

int nums[100005];

void solve(int n) {
    long long pre = 0;
    unordered_map<int, int> left, right;
    // 初始时所有点都在右侧
    for (int i = n - 1; i >= 0; --i)
        right[nums[i]]++;
        
    // 计算每条边的权值
    for (int i = 0; i < n - 1; ++i) {
        // 计算当前边的权值并输出
        printf("%lld%c", pre += n - 2 * i - right[nums[i]] + left[nums[i]], 
               i == n - 2 ? '\n' : ' ');
        // 将当前点从右侧移到左侧
        right[nums[i]]--;
        left[nums[i]]++;
    }
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
        scanf("%d", &nums[i]);
    solve(n);
    return 0;
}
import java.util.*;

public class Main {
    public static void solve(int n, int[] nums) {
        long pre = 0;
        Map<Integer, Integer> left = new HashMap<>();
        Map<Integer, Integer> right = new HashMap<>();
        
        // 初始时所有点都在右侧
        for (int i = n - 1; i >= 0; i--) {
            right.put(nums[i], right.getOrDefault(nums[i], 0) + 1);
        }
        
        // 计算每条边的权值
        for (int i = 0; i < n - 1; i++) {
            pre += n - 2 * i - right.getOrDefault(nums[i], 0) + 
                   left.getOrDefault(nums[i], 0);
            System.out.print(pre + (i == n - 2 ? "\n" : " "));
            
            // 将当前点从右侧移到左侧
            right.put(nums[i], right.get(nums[i]) - 1);
            left.put(nums[i], left.getOrDefault(nums[i], 0) + 1);
        }
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        solve(n, nums);
    }
}
def solve(n, nums):
    pre = 0
    left = {}
    right = {}
    
    # 初始时所有点都在右侧
    for x in nums:
        right[x] = right.get(x, 0) + 1
    
    # 计算每条边的权值
    for i in range(n-1):
        pre += n - 2 * i - right.get(nums[i], 0) + left.get(nums[i], 0)
        print(pre, end='\n' if i == n-2 else ' ')
        
        # 将当前点从右侧移到左侧
        right[nums[i]] -= 1
        left[nums[i]] = left.get(nums[i], 0) + 1

n = int(input())
nums = list(map(int, input().split()))
solve(n, nums)

算法及复杂度

  • 算法:哈希表 + 动态维护
  • 时间复杂度: - 只需要遍历一次数组
  • 空间复杂度: - 需要两个哈希表存储点权计数