[题解] CF549F Yura and Developers
蒟蒻第一次写紫题题解,大佬轻喷。
题目大意:
给定一个数组 \(a\) 和常数 \(k\),求有多少个子区间满足区间和减去区间最大值是 \(k\) 的倍数。
思路:
考虑使用分治解决。
设区间 \([l,r]\) 最大值为 \(mx\)。
对于 \(i(i\le mid)\) 以 \(i\) 为左端点。
钦定 \(mx\) 在左边,这时只需要枚举右端点,满足 \(a_{mid+1\sim r}\) 这段区间的任何一个数不大于 \(mx\),不难发现可以使用双指针维护出满足上述条件的区间 \((mid,R]\)。
令 \(sum\) 数组为前缀和数组。即:
\[sum_i = \begin{cases} sum_i=a_i & i=1\\ sum_i=sum_{i-1}+a_i & i>1 \end{cases}
\]
求 \(r\in(mid,R]\),符合题目条件的个数。
则式子转化为 \(sum_r-sum_{i-1}-mx\equiv 0(mod\ k)\) 符合条件的个数,即 \(sum_r\in sum_{i-1}+mx(mod\ k)\)。
而由于数据很小,\(k\) 只有 \(10^6\),不需要用到 \(map\),开一个桶维护上述式子就可以了。
\(mx\) 在左边的情况搞定了,在右边的情况同理。
复杂度为 \(O(n\log n)\)。
代码:
CP Editor 太好用啦
// Problem: CF549F Yura and Developers
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF549F
// Memory Limit: 250 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
/*+ Nimbunny +*/
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define pi pair<int, int>
// #pragma GCC optimize(2)
using namespace std;
const int INF = INT_MAX;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, m, a[3 * N], sum[3 * N], ans, b[N];
inline int read() {
int x;
cin >> x;
return x;
}
void cdq(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq(l, mid), cdq(mid + 1, r);
int k = mid, ma = 0;
for (int i = mid; i >= l; i--) {
ma = max(ma, a[i]);
while (k != r && a[k + 1] <= ma) {
k++;
b[sum[k] % m]++;
}
ans += b[(sum[i - 1] + ma) % m];
}
for (int i = mid + 1; i <= r; i++) b[sum[i] % m] = 0;
k = mid + 1, ma = 0;
for (int i = mid + 1; i <= r; i++) {
ma = max(ma, a[i]);
while (k != l && a[k - 1] < ma) {
k--;
b[sum[k - 1] % m]++;
}
ans += b[(sum[i] - ma) % m];
}
for (int i = l; i <= mid; i++) b[sum[i - 1] % m] = 0;
return;
}
signed main() {
cin.tie(nullptr)->sync_with_stdio(false);
n = read(), m = read();
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + (a[i] = read());
cdq(1, n);
cout << ans;
return 0;
}

浙公网安备 33010602011771号