首页 > 技术文章 > [USACO]奶牛抗议(DP+树状数组+离散化)

void-f 2017-10-26 14:49 原文

Description

约翰家的N头奶牛聚集在一起,排成一列,正在进行一项抗议活动。第i头奶牛的理智度 为Ai,Ai可能是负数。约翰希望奶牛在抗议时保持理性,为此,他打算将所有的奶牛隔离成 若干个小组,每个小组内的奶牛的理智度总和都要大于零。由于奶牛是按直线排列的,所以 一个小组内的奶牛位置必须是连续的。

请帮助约翰计算一下,存在多少种不同的分组的方案。由于答案可能很大,只要输出答 案除以1,000,000,009的余数即可。

Solution

容易想到设\(F[i]\)表示 到第头\(i\)奶牛的方案数,

那么就有\(F[i]=\sum F[j](sum[i]-sum[j] \geq 0)\), sum为前缀和,

但如果直接枚举时间大概为\(O(n^2)\),显然会超时,那么考虑优化

式子可化为\(F[i]=\sum F[j](sum[i]\geq sum[j])\) ,

可以用前缀和为下标,构造树状数组维护小于等于\(sum[i]\)\(F\) 的和

那么前缀和很大,但是中间有很多没用的,离散化即可

注意\(F[0]=1\)要提前预处理进树状数组,时间复杂度\(O(nlogn)\)

Code

#include <cstdio>
#include <algorithm>
#define LL long long
#define N 1000010
using namespace std;

struct info {
	int id;
	LL sum;
} a[N];
const int yh = 1e9 + 9;
int n, t, p[N];
LL Ans, f[N];

int lowbit(int x) {
	return x & (-x);
}

inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-')f = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}

bool cmp(info a, info b) {return a.sum < b.sum;}

void add(int x, LL v) {
	while (x <= n) {
		f[x] = (f[x] + v) % yh;
		x += lowbit(x);
	}
}

LL cal(int x) {
	LL r = 0;
	while (x) {
		r = (r + f[x]) % yh;
		x -= lowbit(x);
	}
	return r;
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		a[i].sum = a[i - 1].sum + read();
		a[i].id = i;
	}
	a[n + 1].id = n + 1;	//F[0]=1
	a[n + 1].sum = 0;
	sort(a + 1, a + n + 2, cmp);
	for (int i = 1; i <= n + 1; i++) {	//离散化
		if (i == 1 || a[i].sum != a[i - 1].sum) ++t;
		p[a[i].id] = t;
	}
	add(p[n + 1], 1);
	for (int i = 1; i <= n; ++i) {
		Ans = cal(p[i]);
		add(p[i], Ans);
	}
	printf("%lld\n", Ans);
	return 0;
}

推荐阅读