牛客OI周赛6-提高组 A 大法师与魔法石
思路:
对于一个ai, 它可以构成区间[ai/v, ai]
假设和它相邻的为aj, 那么ai 和 aj 构成的区间为[(ai+aj) / v, ai+aj]
那么这两个区间能合并的条件是 (ai + aj) / v <= ai
即aj <= (v - 1)ai (v >= 2)
又因为(v - 1) ai >= ai
所以aj <= ai
所以把每个ai和它相邻的比它小的数连接起来考虑
用单调栈求出每个位置之前和之后第一个比它大的数就可以了
代码:
#pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) #include<bits/stdc++.h> using namespace std; #define fi first #define se second #define pi acos(-1.0) #define LL long long //#define mp make_pair #define pb push_back #define ls rt<<1, l, m #define rs rt<<1|1, m+1, r #define ULL unsigned LL #define pll pair<LL, LL> #define pli pair<LL, int> #define pii pair<int, int> #define piii pair<pii, int> #define mem(a, b) memset(a, b, sizeof(a)) #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define fopen freopen("in.txt", "r", stdin);freopen("out.txt", "w", stout); //head const int N = 1e5 + 5; int a[N], pre[N], nxt[N]; pair<LL, LL> q[N]; LL sum[N]; stack<int> st; int main() { int T, n, v; scanf("%d", &T); while(T--) { scanf("%d %d", &n, &v); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); a[0] = INT_MAX; a[n+1] = INT_MAX; while(!st.empty()) st.pop(); st.push(0); for (int i = 1; i <= n; i++) { while(a[st.top()] <= a[i]) st.pop(); pre[i] = st.top(); st.push(i); } while(!st.empty()) st.pop(); st.push(n+1); for (int i = n; i >= 1; i--) { while(a[st.top()] <= a[i]) st.pop(); nxt[i] = st.top(); st.push(i); } for (int i = 1; i <= n; i++) sum[i] = sum[i-1] + a[i]; for (int i = 1; i <= n; i++) { q[i].fi = (a[i] + v - 1) / v; q[i].se = sum[nxt[i]-1] - sum[pre[i]]; } sort(q+1, q+1+n); LL ans = 0; q[0].se = -1; for (int i = 1; i <= n; i++) { ans += q[i].se - q[i].fi + 1; if(q[i].fi <= q[i-1].se) ans -= q[i-1].se - q[i].fi + 1; } printf("%lld\n", ans); } return 0; }