AcWing 101. 最高的牛
题目链接:101. 最高的牛 - AcWing题库
题目中的 $M$ 对关系带给我们的信息实际上是牛之间身高的相对大小关系。
具体来说,我们建立一个数组 $C$,数组中起初全为 $0$。
若一条关系指明 $A_i$ 和 $B_i$ 可以互相看见(不妨设 $A_i < B_i$),
则把数组 $C$ 中下标为 $A_i + 1$ 到 $B_i - 1$ 的数都减去一,
意思是在 $A_i$ 和 $B_i$ 中间的牛,身高至少要比它们少 $1$。
因为第 $P$ 头牛是最高的,所以最终 $C[P]$ 一定为 0。
其他的牛和第 $P$ 头牛的身高差距就体现在数组 $C$ 中。
换言之,最后第 $i$ 头牛的身高就等于 $H + C[i]$。
如果我们朴素执行把数组 $C$ 中下标为 $A_i + 1$ 到 $B_i - 1$ 到数都减去 $1$ 的操作,
那么整个算法的时间复杂度为 $\mathcal{O}(NM)$,复杂度过高。
一个简单而高效的做法是,额外建立一个数组 $D$,
对于每对 $A_i$ 和 $B_i$,令 $D[A_i + 1]$ 减去 $1$,$D[B_i - 1]$ 加上 $1$。
其含义是:“身高减小 $1$” 的影响从 $A_i + 1$ 开始,持续到 $B_i - 1$,在 $B_i$ 结束。
最后, $C$ 就等于 $D$ 的前缀和,
即 $C[i] = \sum_{j = 1}^i {D[j]}$。
上述优化后的算法把一个区间的操作转化为左、右两个端点上的操作,再通过前缀和得到原问题的解。
这种思想很常用,我们在后面还会多次遇到。
该算法的时间复杂度为 $\mathcal{O}(N + M)$。
在本题的数据中,一条关系 $(A_i, B_i)$ 可能会输入多次,要注意检查,
对于重复的关系,只在第一次出现时执行相关操作即可。
map<pair<int, int>, bool> existed;
int c[10010], d[10010];
int main() {
int n, p, h, m;
cin >> n >> p >> h >> m;
for (int i = 1; i <= m; i ++) {
int a, b;
scanf("%d%d", &a, &b);
if (a > b) swap(a, b);
if (existed[make_pair(a, b)]) continue;
d[a + 1] --, d[b] ++;
existed[make_pair(a, b)] = true;
}
for (int i = 1; i <= n; i ++) {
c[i] = c[i - 1] + d[i];
printf("%d\n", h + c[i]);
}
cout << endl;
}
完整代码:最高的牛.cpp