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;
}

浙公网安备 33010602011771号