1. 程式人生 > 實用技巧 >【YBTOJ】防具佈置

【YBTOJ】防具佈置

題目大意:

\(n\) 組防具。第 \(i\) 個防具會標記在區間 \(\[S_i,E_i\]\) 中模 \(D_i\) 的值是 \(S_i\bmod D_i\) 的位置。求哪個位置被標記了奇數次。

正文:

我們可以考慮二分。那麼找一下題目中隱藏的單調性。

可以發現,我們可以用一段區間的標記總值作為 key,比如當前二分到 \([l,r]\),我們可以先判斷 \([l,mid]\) 的是否為奇數。如果是,則縮短範圍到 \([l,mid]\);若不是,說明 \([mid+1,r]\) 必有一點是奇數,則縮小範圍至 \([mid+1,r]\)

程式碼:

const int N = 2e5 + 10;

int t, n;
ll a[N][3];

ll calc(ll r)
{
	ll ans = 0;
	for (int i = 1; i <= n; i++)
	{
		if (a[i][0] > r) continue;
		ans += (min(r, a[i][1]) - a[i][0]) / a[i][2] + 1;
	}
	return ans;
}

bool check (ll l, ll r)
{
	return (calc(r) - calc(l - 1)) % 2;
}

int main()
{
	ll l, r;
	scanf ("%d", &t);
	for (; t--; )
	{
		scanf ("%d", &n);
		r = 0ll, l = 1ll << 40;
		for (int i = 1; i <= n; i++)
		{
			scanf ("%lld%lld%lld", &a[i][0], &a[i][1], &a[i][2]); 
			l = min (l, a[i][0]); 
			r = max (r, a[i][1]);
		}
		if (!check(l, r)) {puts("There's no weakness."); continue;} 
		while (l <= r)
		{
			ll mid = l + r >> 1ll;
			if (check(l, mid))
				r = mid - 1;
			else
				l = mid + 1;
		}
		printf ("%lld %lld\n", l, calc(l) - calc(l - 1));
	}
    return 0;
}