[CF2053C] Bewitching Stargazer 题解

我们不妨直接递归模拟算答案。
定义 \(f(l, r)\) 表示左右端点为 \(l,r\) 的答案。记 \(mid \gets \lfloor \frac{l+r}{2} \rfloor\),于是:

\[f(l, r) = \begin{cases} f(l, mid)+f(mid+1,r) & (r-l+1) \equiv 0 \pmod 2 \\ f(l, mid-1) + f(mid+1, r) + mid & {\text {otherwise.}} \end{cases} \]

这样做显然会 T,考虑如何优化。我们注意到每次由 \(mid\) 分割,左右两边是对称的。显然,\(mid\) 右边所有取到的点都可以由左边平移得到。设区间长度是 \(len\),这个平移量 \(d\) 就是 \(\lceil \frac{len}{2} \rceil\)。设左(右)边取到了 \(c\) 个点,故右边的答案就是左边的答案加上 \(d \cdot c\)

于是改写一下函数,将求解函数定义为 pair 型,first 存答案,second 存区间内能取到的个数,直接算就好了。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int> 
#define all(v) v.begin(), v.end()
#define int long long
using namespace std;

//#define filename "xxx" 
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)


namespace Traveller {
	
	int n, k;
	pii operator + (pii a, pii b) { return pii(a.first + b.first, a.second + b.second); }
	pii solve(int n) {
		if(n < k) return pii(0, 0);
		pii res = solve(n/2);
		if(n & 1) return res + res + pii(res.second * (1+n >> 1) + (1+n >> 1), 1);
		return res + res + pii(res.second * (n >> 1), 0);
	}
	
	void main() {
		scanf("%lld%lld", &n, &k);
		printf("%lld\n", solve(n).first);
	}
}

signed main() {
	#ifdef filename
		FileOperations();
	#endif
	
	int _;
	cin >> _;
	while(_--) Traveller::main();
	return 0;
}


posted @ 2024-12-29 21:25  Water_M  阅读(63)  评论(0)    收藏  举报