「CH0805」防线

「CH0805」防线

题意

给出一个数列, 里面至多有 1 个奇数, 其他都是偶数,求这个奇数的大小和位置。

是不是看起来很简单?但是这里数列不是直接给出的,会给出 \(n\) 个类型防具, 每个防具有三个参数 \(s, e, d\),分别表示从 \(s\)\(e\) 每隔 \(d\) 有一个防具。

最后的数列就是每个点防具之和。

可能你也猜到了, \(e\) 的大小是小于 \(2_{31}-1\)\(n\le10^8\)

直接用数组模拟的计划没了,即使你开的数组可能包含答案, 但是出题人一定会卡你的,也不必尝试了。

思考一下题目的元素:

  • 一个奇数和若干个偶数
  • 数据范围莫名的大

可以想到二分答案来枚举。

check 的写法

可以想到,如果 \(1, 2, 3, \dots , mid - 1, mid\) 里面有奇数就往这个范围搜, 没有就往 \(mid + 1, mid + 2, mid + 3 \dots , n - 1, n\) 的范围搜。

问题是如何判断一个范围里面是否有奇数?这里保证的还是只有一个奇数。

是不是直接一个前缀和判断一下奇偶就可以了?

新的问题有接踵而至,怎么求前缀和,这奇怪的输入方式应该如何处理?

看下图:

可以发现这里面有 \((e -s) / d\) 个区间,根据小学知识就可以知道里面有 \((e-s)/ d+1\) 条线段,即\((e-s)/ d+1\)个防具。

现在要求 \(1\)\(x\) 的前缀和,到最后一个防具只有下面的两种情况

  • x 被包含在这个区间里面

image

对于这种情况,求 \((x-s)/d+1\) 就可以了,不包含不会求到最后一个 \(d\)

  • x没有被包含在区间里面

image

不看第一个区间,可以发现 \(x\) 被包含到了第二个区间,问题转换到了第一种情况。

于是只用处理第一种情况就可以了。即取 std::min(s, x) 就好了

求答案

第一个答案二分就可以了,在下文中记录为 \(ans\)

第二个答案是求大小,回忆前缀和如何求区间答案, 是不是 sum[r] - sum[l - 1]

在这种情况下 \(l=r\), 答案就是 sum[ans] - sum[ans - 1]

Code
#include <bits/stdc++.h>

using i64 = long long;

inline i64 read(){
	i64 x = 0, f = 1;char ch = getchar();
	while (ch < '0' || ch > '9') f = ch == '-' ? -1 : 1, ch = getchar();
	while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return x * f;
}

constexpr i64 INF = i64_MAX;

i64 main() {
	
	struct node {
		i64 l, r, d;
	};
	
	auto solve = [&] () {
		i64 n = read();
		
		std::vector<node> a(n + 1);
		
		i64 l = 0, r = -1;
		
		for (i64 i = 1; i <= n; i++) {
			a[i].l = read(), a[i].r = read(), a[i].d = read();
			r = std::max(r, a[i].r);
		}
		
		// 计算前缀和 
		std::function<i64(i64)> sum = [&](i64 x) {
			i64 ans = 0;
			for (i64 i = 1; i <= n; i++) {
				if (a[i].l <= x) {
					ans += (std::min(a[i].r, x) - a[i].l) / a[i].d + 1;
				}
			}
			return ans;
		};
		
		i64 ans = -1;
		while (l <= r) {
			i64 mid = l + r >> 1; 
			if (sum(mid) & 1) ans = mid, r = mid - 1;
			else l = mid + 1; 
		}
		i64 ans2 = sum(ans) - sum(ans - 1);
		ans == -1 ? std::cout << "There's no weakness.\n" : std::cout << ans << " " << ans2 << "\n";
	};
	
	i64 T = read();
	while (T--) 
		solve();
	return 0;
}
posted @ 2022-02-08 19:45  落花月朦胧  阅读(135)  评论(0编辑  收藏  举报