【CF】1398D-Segment Intersections 题解

传送门:1398D
标签:数学

题目大意

给你两个线段列表 [[al_1, ar_1], [al_2, ar_2], …, [al_n, ar_n]] 和 [[bl_1, br_1], [bl_2, br_2], …, [bl_n, br_n]]。最初,所有的线段 [al_i, ar_i] 都等于 [l_1, r_1],而所有的线段 [bl_i, br_i] 都等于 [l_2, r_2]。在一个步骤中,你可以选择一条线段(来自第一个或第二个列表)并将其延伸1。换句话说,假设你选择了线段 [x, y],则可以将其转换为 [x-1, y] 或者 [x, y+1]。
定义总相交长度 I 为对应线段对的相交长度之和:∑i=1 n intersection_length([ali,ari],[bli,bri])。空相交长度为0,线段 [x, y] 的长度为 y-x。你需要多少步才能使 I 大于或等于 k?

输入:第一行包含单个整数 t(1 ≤ t ≤ 1000),即测试案例的数量。每个测试案例的第一行包含两个整数 n 和 k (1 ≤ n ≤ 2·105;1 ≤ k ≤ 109)——列表的长度和所需的最小总相交长度。每个测试案例的第二行包含两个整数 l_1 和 r_1 (1 ≤ l_1 ≤ r_1 ≤ 109)——所有线段 [al_i, ar_i] 初始时等同的线段。每个测试案例的第三行包含两个整数 l_2 和 r_2 (1 ≤ l_2 ≤ r_2 ≤ 109)——所有线段 [bl_i, br_i] 初始化时等同的线段。保证了 n 的总和不超过 2·10^5。

输出:输出t个整数。对于每个测试案例,输出所需的最小步骤数,使得I大于或等于k。

算法分析

  • 首先,注意intersection_length 的线段 [l1, r1] 和 [l2, r2] 可以计算为 min(r1, r2) - max(l1, l2)。如果它是负数,则表示两个线段不相交,否则它就是相交部分的长度。现在我们有两种主要情况:线段 [l1, r1] 和 [l2, r2] 是否已经相交?如果线段相交,那么我们已经有了 n * (min(r1, r2) - max(l1, l2)) 作为总相交部分。很显然,在每一步中使两个线段等于[min(l1, l2), max(r1, r2)] 总是最佳选择,因为这样可以将总相交部分增加1。
  • 在使所有线段等于[min(l1, l2), max(r1, r2)] 后,我们可以使用两步将总相交部分增加1:我们需要延长一对中的两个线段。因此,我们可以找到一个简单的公式来计算最小步骤数:我们已经有n * (min(r1, r2) - max(l1, l2)) 的总相交部分,然后我们可以使用一个步骤将其增加到最多n * ((max(r1, r2) - min(l1, l2)) - (min(r1, r2) - max(l1, l2))) ,然后可以增加到任何数字,使用两个步骤进行每次增加。
  • 对于不相交的线段 [l1, l2] 和 [r1, r2],我们应该首先“投资”一些步骤在每个对中使其相交。所以让我们迭代“投资”的线段数量 cntInv。我们应该执行 cntInv * (max(l1, l2) - min(r1, r2)) 步骤来让线段接触。现在,cntInv 条线段接触,所以我们可以在前一种情况下几乎使用相同的公式。总体复杂度为 O(n) 。

代码实现

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int M1 =  998244353;
const int M2 =  1000000007;

void solve() {
	int n, k, ans = 0;
	cin >> n >> k;
	int l[2], r[2];
	for(int i : {0, 1})
		cin >> l[i] >> r[i];
	if(l[0] > l[1])
		swap(l[0], l[1]), swap(r[0], r[1]);
	int x = max((int)0, min(r[0], r[1]) - l[1]);
	k -= x * n;
	for(int i = 1; i <= n && k > 0; i++) {
		int y = min(k, max(r[1], r[0]) - min(l[1], l[0]) - x);
		int cost = max((int)0, l[1] - r[0]) + y;
		if(i >= 2)
			cost = min(cost, 2 * y);
		ans += cost;
		k -= y;
	}
	if(k > 0)
		ans += k * 2;
	cout << ans << "\n";
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--)solve();
}
  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值