CF1042D Petya and Array

这题有两种做法 大概都是 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;
}

权值线段树带离散化有风险,做题需谨慎啊

posted @ 2018-09-18 18:11  EvalonXing  阅读(261)  评论(0编辑  收藏  举报