Codeforces Round 975 (Div. 2)

Codeforces Round 975 (Div. 2) B题题解

B
题目链接
发现有些题解写的不太容易理解,代码没讲清楚,所以有了下面这篇题解

分析:
根据题目可以得知区间如下
[ x 1 , x 2 ] , [ x 1 , x 3 ] , [ x 1 , x 4 ] … [ x 1 , x n ] [x_1, x_2], [x_1, x_3], [x_1, x_4]\ldots[x_1, x_n] [x1,x2],[x1,x3],[x1,x4][x1,xn]
[ x 2 , x 3 ] , [ x 2 , x 4 ] , [ x 2 , x 5 ] … [ x 2 , x n ] [x_2, x_3], [x_2, x_4], [x_2, x_5]\ldots[x_2, x_n] [x2,x3],[x2,x4],[x2,x5][x2,xn]
.
.
.
[ x n − 2 , x n − 1 ] , [ x n − 2 , x n ] [x_{n-2},x_{n-1}], [x_{n-2},x_n] [xn2,xn1],[xn2,xn]
[ x n − 1 , x n ] [x_{n-1},x_n] [xn1,xn]
对于 k k k这个点,要求它被那些区间所包括,分两种情况来看:

  • 如果 k k k是某一个 x i x_i xi,那么有如下的情况

    1. k k k是一些区间的左端点的情况,那么有几个 x i x_i xi以它为左端点呢,就是 n − i n-i ni个,分别是 { x i + 1 , x i + 2 , x i + 3 , … , x n x_{i+1},x_{i+2}, x_{i+3},\ldots, x_n xi+1,xi+2,xi+3,,xn} ; ;
    2. 有几个点 x i x_i xi k k k为右端点呢,就是 i − 1 i-1 i1个,分别是
      { x 1 , x 2 , x 3 , … , x i − 1 x_{1},x_{2}, x_{3},\ldots, x_{i-1} x1,x2,x3,,xi1}
    3. 有几个区间包含 k k k这个点呢,就是 ( i − 1 ) ∗ ( n − i ) (i-1)*(n-i) (i1)(ni)个,分别是左端点:{ x 1 , x 2 , x 3 , … , x i − 1 x_{1},x_{2}, x_{3},\ldots, x_{i-1} x1,x2,x3,,xi1},和右端点:
      { x i + 1 , x i + 2 , x i + 3 , … , x n x_{i+1},x_{i+2}, x_{i+3},\ldots, x_n xi+1,xi+2,xi+3,,xn}
  • 如果 k k k是在 ( x i , x i + 1 ) (x_i, x_{i+1}) (xi,xi+1)这个开区里的一个点,那么有几个区间包含它呢?可以看有几个左端点,有几个右端点,这些左端点右端点两两配对会包括 k k k这个点,即 i i i个左端点,分别是{ x 1 , x 2 , x 3 , … , x i x_1, x_2, x_3,\ldots, x_i x1,x2,x3,,xi},和 ( n − i ) (n - i) (ni)个右端点,分别是{ x i + 1 , x i + 2 , x i + 3 , … , x n x_{i+1}, x_{i+2}, x_{i+3},\ldots, x_n xi+1,xi+2,xi+3,,xn},两两配对,那么 k k k这个点就被 i ∗ ( n − i ) i*(n-i) i(ni)这么多个区间包括

所以可以设 m x m_x mx为恰好被 x x x个区间包括的点有几个。在刚才讲的第一种情况就是在求 m i − 1 + n − i + ( i − 1 ) ∗ ( n − 1 ) m_{i-1+n-i+(i-1)*(n-1)} mi1+ni+(i1)(n1),可以简化成 m n − 1 + ( i − 1 ) ∗ ( n − 1 ) m_{n-1+(i-1)*(n-1)} mn1+(i1)(n1),有些题解直接告诉你简化后的,没有一步步告诉你怎么来的。刚才讲的第二种情况就是在求 m i ∗ ( n − i ) m_{i*(n-i)} mi(ni),于是可以来预处理 m m m,其余看代码

#include <bits/stdc++.h>

#define int long long // 不开long long见祖宗

using namespace std;

signed main() {
	int _ = 1;
	cin >> _;

	while (_ -- ) {
		int n, q;
		cin >> n >> q; // 输入

		vector<int> x(n + 1);
		map<int, int> m; // m[x]表示恰好被x个区间所包括的点有多少个

		for (int i = 1; i <= n; i++) cin >> x[i]; // 输入
		// 预处理
		for (int i = 1; i <= n; i++) {
			m[i - 1 + n - i + (i - 1) * (n - i)] ++ ; // 第一种情况

			if (i < n) m[i * (n - i)] += (x[i + 1] - x[i] - 1); // 第二种情况
		}

		while (q -- ) {
			int k;
			cin >> k; // 询问

			cout << m[k] << ' '; // 输出
		}

		cout << '\n'; // 输出
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值