洛谷P2344 奶牛抗议
题目背景
Generic Cow Protests, 2011 Feb
题目描述
约翰家的N 头奶牛正在排队游行抗议。一些奶牛情绪激动,约翰测算下来,排在第i 位的奶牛的理智度为Ai,数字可正可负。
约翰希望奶牛在抗议时保持理性,为此,他打算将这条队伍分割成几个小组,每个抗议小组的理智度之和必须大于或等于零。奶牛的队伍已经固定了前后顺序,所以不能交换它们的位置,所以分在一个小组里的奶牛必须是连续位置的。除此之外,分组多少组,每组分多少奶牛,都没有限制。
约翰想知道有多少种分组的方案,由于答案可能很大,只要输出答案除以1000000009 的余数即可。
输入输出格式
输入格式:
• 第一行:单个整数N,1 ≤ N ≤ 100000
• 第二行到第N + 1 行:第i + 1 行有一个整数Ai,−10^5 ≤ Ai ≤ 10^5
输出格式:
单个整数:表示分组方案数模1000000009 的余数
输入输出样例
4 2 3 -3 1
4
说明
解释:如果分两组,可以把前三头分在一组,或把后三头分在一组;如果分三组,可以把中间两头分在一组,第一和最后一头奶牛自成一组;最后一种分法是把四头奶牛分在同一组里。
分析:求方案数的解决方法可以用数学方法和dp,这道题我找不出数学数学方法来,只能dp.本来我的状态考虑为f[i][j]表示前j头牛分i组,可是这个i并不确定,而且O(n^3)的dp,直接超时了,那么我们去掉这一维,f[i]表示前i头牛的分组,f[i]=∑f[j](a[j] + a[j + 1] +...+a[i] >= 0),转换成前缀和就是s[j] <= s[i],那么怎么找j < i并且s[j] <= s[i]的数呢?当然是树状数组啦!但是由于s可能过大,需要先离散化一下.
dp中如果状态中的一维不能确定,可以省去这一维,通常可以用树状数组优化满足两个条件i<j&&s[i]<s[j]的题,数据太大需要离散化.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int mod = 1000000009; int n,cnt = 1,ans,c[100010]; struct node { int a,id,newid; }e[100010]; bool cmp(node a,node b) { return a.a < b.a; } bool cmp2(node a,node b) { return a.id < b.id; } void add(int x,int d) { while (x <= cnt) { c[x] = (c[x] + d) % mod; x += x & (-x); } } int query(int x) { int res = 0; while (x) { res = (res + c[x]) % mod; x -= x & (-x); } return res; } int main() { scanf("%d",&n); e[0].a = 0; e[0].id = 0; for (int i = 1;i <= n; i++) { int t; scanf("%d",&t); e[i].a = e[i-1].a + t; e[i].id = i; } sort(e,e + 1 + n,cmp); e[0].newid = 1; for (int i = 1; i <= n; i++) { if (e[i].a != e[i-1].a) e[i].newid = ++cnt; else e[i].newid = cnt; } sort(e,e + 1 + n,cmp2); add(e[0].newid,1); for (int i = 1; i <= n; i++) { ans = query(e[i].newid); add(e[i].newid,ans); } printf("%d\n",ans); return 0; }