CF2086B 学习笔记

题意

对于每个测试用例:
给定一个长度为 \(n\) 的数组 \(a_n\)\(k\),按照如下要求构造长度为 \(nk\) 的数组 \(b_{nk}\)

  • \(\forall 1 \le i \le n, b_i=a_i\)
  • \(\forall {n + 1} \le i \le nk, b_i=b_{i-n}\)

再给定一个 \(x\),统计所有的左端点 \(l\),满足

\[\exists r \ge l,\sum_{i=l}^{r}b_i \ge x \]


先说说 \(b\) 数组的构造,首先题目中的例子:

\[a=\{2,3,1,4\},k=3 \]

构造出的 \(b\) 数组为

\[b=\{2,3,1,4,2,3,1,4,2,3,1,4\} \]

其实就是 \(a\) 数组反复 \(k\) 遍,将上面的情况一般化就可以证明。

\(a\) 数组为 \(a_1, a_2, \dots, a_n\),则 \(b\) 数组的前 \(n\) 个为 \(a_1, a_2, \dots, a_n\),再根据构造的第二条,

\[\begin{cases} b_{n+1}=b_{n+1-n}=b_1=a_1\\ b_{n+2}=b_{n+2-n}=b_2=a_2\\ \dots\\ b_{nk}=b_{nk-n}=b_{n(k-1)}=a_n \end{cases} \]

证毕。


接下来就是左端点 \(l\) 的寻找了。

考虑先计算前缀和和总和。

\[sum=\sum_{i=1}^{n}a_i \]

寻找周期即可,更详细的解释在代码里。

code

#include <iostream>
#define Ofile(s) freopen(s".in", "r", stdin), freopen (s".out", "w", stdout)
#define Cfile(s) fclose(stdin), fclose(stdout)
#define fast ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define maxn 100005
#define int long long
using namespace std;

int t, n, k, x;

int a[maxn], pre[maxn];

signed main() {
	cin >> t;
	while (t--){
		cin >> n >> k >> x;
		for (int i = 0; i < n; i++) 
			cin >> a[i];
		for (int i = 0; i < n; i++)
			pre[i + 1] = pre[i] + a[i];//前缀和
		int total = pre[n];
		int ans = 0;
		// 对于每个起始位置在a中的索引i (0-based)
		for (int i = 0; i < n; i++) {
			/*对于每个周期p (0-based)
			起始位置: l = i + p*n + 1
			我们需要找到最小的完整周期数c,使得:
			从位置i开始,加上c个完整周期的总和 >= x
			先从当前位置到当前周期结束*/
			int cur_sum = pre[n] - pre[i];
			if (cur_sum >= x) {// 不需要跨周期
				ans += k;  // 在这个位置的所有周期都满足条件
				continue;
			}
			// 需要跨周期
			int need = x - cur_sum;
			if (total <= 0) // 如果total <= 0,无法通过增加周期来满足
				continue;
			// 需要的完整周期数
			int need_cycles = (need + total - 1) / total;
			// 如果需要的周期数 <= 剩余的周期数
			if (need_cycles <= k - 1) // 在这个位置的所有周期中,满足条件的周期数
				ans += (k - need_cycles);
		}
		
		cout << ans << "\n";
	}
	return 0;
}
posted @ 2026-02-02 19:04  constexpr_ll  阅读(0)  评论(0)    收藏  举报