题目链接:http://codeforces.com/problemset/problem/877/F
题目大意:有n本书排成一个序列, 每本书属于1类或2类, 第i本书上有 ai 道题。 给定一个k, 一段可购买的子区间[l,r]需要满足1类书的题目总数刚好比2类书的题目总数多k道。有q组询问,每次询问[l,r]中有多少个子区间满足该条件。 (n≤105,−109≤k≤109,0≤ai≤109,q≤105)
思路:让1类书a[i] = a[i],2类书a[i] = -a[i],求一个前缀和sum后, 对于一个sum[i], 只需要知道合法的sum[i] - k的值出现了几次。
考虑如果已知了[l, r]的答案, 通过维护当前每个前缀和的值出现的次数, 我们可以在O(1)的时间内求出[l - 1, r]或[l + 1, r]或[l, r - 1]或[l, r + 1]的答案, 实现答案区间的转移。 可以用莫队算法在 O(qn‾‾√) 解决所有的询问。
也就是将询问[l, r]按照以 ln√ 为第一关键字, r为第二关键字, 排序后依次处理即可。
PS:通过离散化和预处理每个sum[i], sum[i] + k, sum[i] - k对应的哈希值后, 可以让询问的主题过程中不带log。
#include <map>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mid ((l + r) >> 1)
using namespace std;
const int N = (int)2e5 + 10;
const int M = 300;
int n, k, q, a[N], t[N]; ll sum[N];
int tot, cnt[N * 4], x[N], pre[N], suf[N]; ll c[N * 4], output[N];
struct Query{
int l, r, id;
bool operator<(const Query &_) const{
if (l / M != _.l / M) return l / M < _.l / M;
return r < _.r;
}
}que[N];
int main(){
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i ++) scanf("%d", t + i);
for (int i = 1; i <= n; i ++) scanf("%d", a + i);
c[++ tot] = 0; c[++ tot] = k; c[++ tot] = -k;
for (int i = 1; i <= n; i ++){
sum[i] = sum[i - 1] + (t[i] == 1 ? a[i] : -a[i]);
c[++ tot] = sum[i]; c[++ tot] = sum[i] - k; c[++ tot] = sum[i] + k;
}
sort(c + 1, c + tot + 1);
tot = unique(c + 1, c + tot + 1) - c - 1;
for (int i = 0; i <= n; i ++){
x[i] = lower_bound(c + 1, c + tot + 1, sum[i]) - c;
pre[i] = lower_bound(c + 1, c + tot + 1, sum[i] - k) - c;
suf[i] = lower_bound(c + 1, c + tot + 1, sum[i] + k) - c;
}
scanf("%d", &q);
for (int i = 1; i <= n; i ++) scanf("%d %d", &que[i].l, &que[i].r), que[i].id = i;
sort(que + 1, que + q + 1);
int L = 0, R = 1; ll ans = k == sum[1];
cnt[x[0]] ++; cnt[x[1]] ++;
for (int i = 1; i <= q; i ++){
int l = que[i].l - 1, r = que[i].r, id = que[i].id;
while (l < L){
ans += cnt[suf[L - 1]]; cnt[x[L - 1]] ++; L --;
}
while (l > L){
cnt[x[L]] --; ans -= cnt[suf[L]]; L ++;
}
while (r < R){
cnt[x[R]] --; ans -= cnt[pre[R]]; R --;
}
while (r > R){
ans += cnt[pre[R + 1]]; cnt[x[R + 1]] ++; R ++;
}
output[id] = ans;
}
for (int i = 1; i <= q; i ++) printf("%lld\n", output[i]);
return 0;
}