4315 两个数列(区间相交)

1. 问题描述:

有两个正整数数列 a1,a2,…,an 和 b1,b2,…,bn。现在,已知的信息有:
数列 a 的各个元素的值;
数列 b 的各个元素之和 s;
对于任意的 1 ≤ i ≤ n,满足 1 ≤ bi ≤ ai 成立;
利用给出的信息,我们可以对数列 b 中各个元素的值进行推断。由上述信息,我们可知对于元素 bi,其可能的取值范围为 [1,ai],但是受到已知条件的约束,它可能无法取到其中一些数值。我们的任务就是计算每个 bi 在其可能的取值范围内,无法取到的数值的数量。例如,如果 n = 2,a ={4,4},s = 8,则数列 b 中的每一个元素都不能小于 4(否则,另一个元素就要大于 4,这是不可能的),也就是说 b1 和 b2 在其可能的取值范围 [1,4] 内,均无法取到 1,2,3,无法取到的数值的数量均为 3。

输入格式

第一行包含两个整数 n 和 s。第二行包含 n 个整数 a1,a2,…,an。

输出格式

共一行,n 个整数,其中第 i 个整数表示 bi 在其可能的取值范围内,无法取到的数值的数量。

数据范围

前三个测试点满足 1 ≤ n ≤ 2。
所有测试点满足 1 ≤ n ≤ 2 × 10 ^ 5,n ≤ s ≤ ∑i=1n ai,1 ≤ ai ≤ 10 ^ 6。

输入样例1:

2 8
4 4

输出样例1:

3 3

输入样例2:

1 3
5

输出样例2:

4

输入样例3:

2 3
2 3

输出样例3:

0 1
来源:https://www.acwing.com/problem/content/4318/

2. 思路分析:

分析题目可以知道对于每一个 bi,我们需要求解出在 [1,a[i]] 范围中满足条件的最小数字和最大数字,在这个范围内的所有数字都是可以取到的,范围外的数字不可以取到;对于当前的 bi,当前最小数字为其余数字都取对应的 a[i] 的时候,也即 t = s - (sum - a[i]),sum 为 [1,n] 中 ai 的和,当 t > a[i] 说明当前最小的数字都大于 a[i],但是对于这道题目来说是不存在的(因为对于 b 中所有元素的和每一个都取 a[i],那么总和为 a 中所有元素的和,所以对于每一个 b[i] 肯定不会超过 a[i]);当 t > 1的时候说明当前最小的数字都需要大于1,那么[1,t - 1]的数字不可以取到;对于当前的 b[i],当前最大数字为其余数字都取 1 也即 s - (n - 1) 的时候,当 a[i] > s - (n - 1) 的时候说明[a[i] - s + n,a[i]] 中的数字不可以取到,累加两种情况就是当前 b[i] 不可以取到的数字个数;由上面的分析其实可以知道本质上是一个数学问题,我们需要求解两个区间的交集,并且由题目的限制条件可以知道两个区间是一定存在交集的:

3. 代码如下:

go(超时:感觉应该是输入输出的问题):

package main

import "fmt"

func main() {
	const N = 200010
	var (
		n, sb int
		a     [N]int
	)
	fmt.Scan(&n, &sb)
	sa := 0
	for i := 0; i < n; i++ {
		fmt.Scan(&a[i])
		sa += a[i]
	}
	for i := 0; i < n; i++ {
		x := a[i]
		count := 0
		t := sb - (sa - x)
		if t > 1 {
			count += t - 1
		}
		if x > sb-(n-1) {
			count += x - sb + n - 1
		}
		fmt.Print(count, " ")
	}
}
package main

import "fmt"

func min(a, b int64) int64 {
	if a < b {
		return a
	}
	return b
}

func max(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

func get(a int64, b int64, c int64, d int64) int64 {
	return min(b, d) - max(a, c) + 1
}

func main() {
	const N = 200010
	var (
		n  int
		sb int64
		a  [N]int64
	)
	fmt.Scan(&n, &sb)
	sa := int64(0)
	for i := 0; i < n; i++ {
		fmt.Scan(&a[i])
		sa += a[i]
	}
	for i := 0; i < n; i++ {
		fmt.Print(a[i] - get(1, a[i], sb-(sa-a[i]), sb-int64(n-1)), " ")
	}
}

python:

class Solution:
    def process(self):
        # s为b中所有元素的和
        n, sb = map(int, input().split())
        a = list(map(int, input().split()))
        sa = 0
        for x in a:
            sa += x
        # 对于每一个数字求解当前最小和最大的对应数字是多少
        for i in range(n):
            x = a[i]
            t = sb - (sa - x)
            count = 0
            # t > x 说明其余的数字都取到最大的时候都不可以满足要求说明全部数字都不符合要求
            if t > 1:
                count += t - 1
            # 求解当前最大不能够超过多少, 其余数字都取1所以为总和为n - 1, 那么当 x > sb - (n - 1) 的时候说明不能够取到的数字个数为x - sb + n - 1
            if x > sb - (n - 1):
                count += x - sb + n - 1
            print(count, end=" ")


if __name__ == '__main__':
    Solution().process()
class Solution:
    def get(self, a: int, b: int, c: int, d: int):
        return min(b, d) - max(a, c) + 1

    def process(self):
        n, sb = map(int, input().split())
        a = list(map(int, input().split()))
        sa = 0
        for x in a:
            sa += x
        for x in a:
            print(x - self.get(1, x, sb - (sa - x), sb - (n - 1)), end=" ")


if __name__ == '__main__':
    Solution().process()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值