CF877F Ann and Books (分类统计贡献+普通莫队)
CF877F Ann and Books
题意:
商店里有 \(n\) 本书,每本书中有 \(a_i\) 个 \(t_i=1/2\) 类问题。
\(m\) 次询问,每次询问给出一个区间,求有多少个符合下列条件的区间:
- 这个区间是给出区间的子区间
- 这个区间的所有书中第 \(1\) 类问题比第 \(2\) 类问题多 \(k\) 个,其中 \(k\) 在所有询问中相同。
抽象一下,也就是求 \([L, R]\) 内有多少个子区间 \([l, r]\) 满足
\[\sum_l^r a[1][i] = \sum_l^r a[2][i] + k
\]
即
\[s[1][r] - s[1][l - 1] = s[2][r] - s[2][l - 1] + k
\]
\[s[1][r] - s[2][r] = s[1][l - 1] - s[2][l - 1] + k
\]
令 \(v[i] = s[1][i] - s[2][i]\),将原问题转化为 \([L - 1, R]\) 内多少对 \((i, j)\) 满足
- \(i < j\)
- \(v[i] + k = v[j]\)
用莫队维护。
如果在现有区间左侧加入一个数 \(x\),则 \(x\) 只能作为区间左端点,答案加上当前的 \(cnt[x + k]\),删除同理。
如果在现有区间右侧加入一个数 \(x\),则 \(x\) 只能作为区间右端点,答案加上当前的 \(cnt[x - k]\),删除同理。
如果 \(k = 0\),由于自己和自己不能产生贡献,需考虑更新答案和更新 \(cnt\) 的顺序。
离散化的时候注意把 \(v - k\) 和 \(v + k\) 也放进去。
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++ i)
#define per(i, a, b) for(int i = (a); i >= (b); -- i)
#define pb emplace_back
#define All(X) X.begin(), X.end()
using namespace std;
using ll = long long;
constexpr int N = 1e5 + 5, B = 300;
#define c(x) ((x) / B)
struct Node {
int l, r, id;
bool operator < (const Node &x) {
if(c(l) != c(x.l)) return c(l) < c(x.l);
if(c(l) & 1) return c(r) < c(x.r);
return c(r) > c(x.r);
}
} q[N];
ll n, m, k, t[N], v[N], lv[N], rv[N], b[N * 3], cnt[N * 3], idx;
ll res, ans[N];
void add(int x, int y) {
res += cnt[y], ++ cnt[x]; // 先加上贡献,再更新数值,防止 x = y,del同理
}
void del(int x, int y) {
-- cnt[x], res -= cnt[y];
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> k;
rep(i, 1, n) cin >> t[i];
rep(i, 1, n) {
cin >> v[i];
v[i] = v[i - 1] + (t[i] == 1 ? 1 : -1) * v[i];
}
rep(i, 0, n) {
b[++ idx] = v[i];
b[++ idx] = v[i] - k;
b[++ idx] = v[i] + k;
}
sort(b + 1, b + idx + 1);
int tot = unique(b + 1, b + idx + 1) - b - 1;
rep(i, 0, n) {
lv[i] = lower_bound(b + 1, b + tot + 1, v[i] - k) - b;
rv[i] = lower_bound(b + 1, b + tot + 1, v[i] + k) - b;
v[i] = lower_bound(b + 1, b + tot + 1, v[i]) - b;
}
cin >> m;
rep(i, 1, m) cin >> q[i].l >> q[i].r, q[i].id = i;
sort(q + 1, q + m + 1);
int l = 0, r = -1;
rep(i, 1, m) {
auto[L, R, id] = q[i];
-- L;
while(l < L) del(v[l], rv[l]), ++ l;
while(l > L) -- l, add(v[l], rv[l]);
while(r < R) ++ r, add(v[r], lv[r]);
while(r > R) del(v[r], lv[r]), -- r;
ans[id] = res;
}
rep(i, 1, m) cout << ans[i] << '\n';
return 0;
}