本题的思路感觉没有那么难,但是最麻烦的是理解题目中的数学公式。在题目中r所需要满足的两个要求其实是A从i开始的累加和直到到达r的位置使小于等于k*c,到r+1的位置大于k*c。
那么我们可以计算A的前缀和,然后在使用upper_bound的时候将查找的值加上a[i-1],这样就实现了在i到最后的位置的前缀和的查找。
然后考虑题目中说的链接每次操作可以选择一个下标 让c对应下标的值加一。让c的值加一的话可能会使在这个下标下的r值增大,但绝对不会减小。所以我们想要max-min最小就需要去改变min的值。在这里使用一个优先队列去维护每个堆顶都是最小的B值。然后去改变这个下标下的c值重新计算r值。之后将得到的新数据维护最大最以及答案的值。
//那两个要求就相当于找最远的插入位置 //计算A的前缀和可以使用upper_bound来二分查找ri的值,由于c的增加不会让最大的B值变小,所以采用优先队列去实现一B的值为小根堆的优先队列。 //每次操作就从优先队列里面取出堆顶的元素,让其对应的C坐标加一重新计算 #include <bits/stdc++.h> using namespace std; #define int long long const int maxn = 2e5+10; struct Node { int posi; int val; bool operator < (const Node &nd) const { return val>nd.val; } }; int a[maxn], k[maxn], c[maxn]; int r[maxn]; priority_queue<Node> pq; int rd() { int num=0, k = 1; char ch = getchar(); while (ch=='-') { k = k^1; ch = getchar(); } while (ch>='0'&&ch<='9') { num = num*10+(ch-'0'); ch = getchar(); } return num; } signed main() { int n, m; n = rd();m = rd(); for (int i=1;i<=n;i++) { a[i] = rd(); a[i] = a[i] + a[i-1]; } for (int i=1;i<=n;i++) { k[i] = rd(); } for (int i=1;i<=n;i++) { c[i] = rd(); } int Max = INT_MIN; //开始初步计算r以及数组b的值。 for (int i=1;i<=n;i++) { int pos = upper_bound(a+i, a+n+1, a[i-1]+k[i]*c[i])-a; r[i] = pos-1; pq.push({i, r[i]-i+1}); Max = max(Max, r[i]-i+1); } int ans = Max - pq.top().val; //给了m次机会,每次都对对小的值进行操作,因为对最大的值进行操作只能变得更大没有意义。 //因为情况并不单调,所以得在计算过程中进行记录更新 while (m--) { Node nd = pq.top(); int i = nd.posi; if (r[i]==n) continue; pq.pop(); c[i]++; int pos = upper_bound(a+i, a+n+1, a[i-1]+k[i]*c[i])-a; r[i] = pos-1; pq.push({i, r[i]-i+1});Max = max(Max, r[i]-i+1); ans = min(ans, Max - pq.top().val); } cout<<ans; return 0; }