这题有两种做法 大概都是 O(nlogn) 的
解法一:权值线段树
先列一下式子发现要求的就是 对于所有的 R ,求出满足 sum[R] < sum[L - 1] + t 的 L 的个数
这可以权值线段树做,就像是这道题
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#define lson t[x].ch[0]
#define rson t[x].ch[1]
using namespace std;
typedef long long ll;
const int MAXN = 200005;
struct Node{
int ch[2];
ll sum;
Node() {sum = 0;}
}t[MAXN << 2];
int n, ptr, top, sizb;
ll T, res;
int a[MAXN];
ll pre[MAXN], uni[(MAXN << 1) + 2333];
inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
f = (c == '-');
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + (c ^ 48);
c = getchar();
}
return f ? -x : x;
}
inline ll rdll() {
register ll x = 0ll;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
f = (c == '-');
c = getchar();
}
while(isdigit(c)) {
x = x * 10ll + (c ^ 48);
c = getchar();
}
return f ? -x : x;
}
int build(int l, int r) {
int x = ++ptr;
t[x].sum = 0;
if(l == r) return x;
int mid = ((l + r) >> 1);
lson = build(l, mid);
rson = build(mid + 1, r);
return x;
}
inline void pushup(int x) {
t[x].sum = t[lson].sum + t[rson].sum;
return;
}
void update(int dst, int l, int r, int x) {
if(l == r) {
++t[x].sum;
return;
}
int mid = ((l + r) >> 1);
if(dst <= mid) update(dst, l, mid, lson);
else update(dst, mid + 1, r, rson);
pushup(x);
return;
}
ll query(int L, int R, int l, int r, int x) {
if(L <= l && r <= R) return t[x].sum;
int mid = ((l + r) >> 1);
ll ans = 0ll;
if(L <= mid) ans = query(L, R, l, mid, lson);
if(mid < R) ans += query(L, R, mid + 1, r, rson);
return ans;
}
int main() {
n = rd(); T = rdll();
for(int i = 1; i <= n; ++i) {
a[i] = rd();
pre[i] = pre[i - 1] + a[i];
uni[++top] = pre[i] + 1;
uni[++top] = pre[i] + T;
}
uni[++top] = T;
sort(uni + 1, uni + top + 1);
sizb = unique(uni + 1, uni + top + 1) - uni - 1;
build(1, sizb);
update(lower_bound(uni + 1, uni + sizb + 1, T) - uni, 1, sizb, 1);
for(int i = 1; i <= n; ++i) {
int dstR = lower_bound(uni + 1, uni + sizb + 1, pre[i] + 1) - uni;
int dst = lower_bound(uni + 1, uni + sizb + 1, pre[i] + T) - uni;
res += query(dstR, sizb, 1, sizb, 1);
update(dst, 1, sizb, 1);
}
printf("%lld\n", res);
return 0;
}
解法二:分治
此题也可分治,每次分治下去,只计算跨过 mid 的答案个数
算出 [l, mid] 的后缀和 suf,算出 [mid + 1, r] 的前缀和 pre
并将两个数组排序,排序后就可以保证其中一个值递增时合法的另一个值是递减的,就可以双指针扫了
如果写基数排序的话它就是 O(nlogn) 的了
代码:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
using namespace std;
typedef long long ll;
const int MAXN = 200005;
int n;
ll t, ans;
int a[MAXN];
ll suf[100005], pre[MAXN];
inline int rd() {
register int x = 0;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
f = (c == '-');
c = getchar();
}
while(isdigit(c)) {
x = x * 10 + (c ^ 48);
c = getchar();
}
return f ? -x : x;
}
inline ll rdll() {
register ll x = 0ll;
register char c = getchar();
register bool f = false;
while(!isdigit(c)) {
f = (c == '-');
c = getchar();
}
while(isdigit(c)) {
x = x * 10ll + (c ^ 48);
c = getchar();
}
return f ? -x : x;
}
void dvdcq(int l, int r) {
if(l == r) {
ans += (a[l] < t);
return;
}
int mid = ((l + r) >> 1);
suf[mid] = a[mid];
pre[mid] = 0;
for(int i = mid - 1; i >= l; --i) suf[i] = suf[i + 1] + a[i];
for(int i = mid + 1; i <= r; ++i) pre[i] = pre[i - 1] + a[i];
sort(suf + l, suf + mid + 1);
sort(pre + mid + 1, pre + r + 1);
int sp = l, pp = r;
for( ; sp <= mid; ++sp) {
while(pp >= mid + 1 && suf[sp] + pre[pp] >= t) --pp;
ans += (ll)(pp - mid);
}
dvdcq(l, mid);
dvdcq(mid + 1, r);
return;
}
int main() {
n = rd(); t = rdll();
for(int i = 1; i <= n; ++i) a[i] = rd();
dvdcq(1, n);
printf("%lld\n", ans);
return 0;
}
权值线段树带离散化有风险,做题需谨慎啊