SicilyOJ(SOJ) 5228 Generic Cow Protests(dp+离散化+树状数组优化)

本文介绍了一种关于DP算法的问题及优化方法,通过树状数组记录并更新子问题解的数量,解决给定序列的有效划分问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目大意】:给出n个数,现在可以按顺序随意的把n个数划分成任意部分,问使得每一部分的和均不小于0的划分方式有多少种。


【解题思路】:dp的转移方程很显然,dp[i]表示前i个数,对其进行任意划分之后满足题设的种类有多少种。则dp[j]=sigema(dp[i]) (0<=i<j && sum[i]<=sum[j]) 这里sum指的是前n个数的和。其中dp[0]=1;

然后...这样写显然会超时....我们要进行优化。-_-!!!!!~...

我们可以发现,其实每一次dp值的更改其实只是跟前面sum小于等于其当前所在的sum值的时候发生,而且对于相同的sum值所对应的,可以在前一个的基础上进行修改

5

2 3 -5 2 1为例...

sum 0 2 5 0 2 3

dp     1 1 2 1 3 6 

  我们无需理会两个相同的sum之间相隔多少个数...我们只需要知道比其小的sum值有多少就可以了...也就是可以按顺序更新dp值...像什么...树状数组...

  c[]来存储树状数组,我们首先先对sum进行离散,并去重,因为我们关心的是比其小的数,相等的数可以根据比其小的数进行更新。

  也就是,我们每一步都可以得到一个dp值,它是小于等于当前sum的方法的和。然后我们再更新当前点的方法数就可以了....说得各种混乱...看代码吧...


【代码】:

   

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#include <string>
#include <cctype>
#include <map>
#include <iomanip>
                   
using namespace std;
                   
#define eps 1e-8
#define pi acos(-1.0)
#define inf 1<<30
#define linf 1LL<<60
#define pb push_back
#define lc(x) (x << 1)
#define rc(x) (x << 1 | 1)
#define lowbit(x) (x & (-x))
#define ll long long
#define mod 1000000009
 
int n,a[100005],c[100005];
int sum[100005],summ[100005],cnt;
 
void update(int x,int val){
    for (int i=x; i<=cnt; i+=lowbit(i))
        c[i]=(c[i]+val)%mod;
}

int getsum(int x){
    int sum=0;
    for (int i=x; i>0; i-=lowbit(i))
        sum=(sum+c[i])%mod;
    return sum;
}

int find(int x){
    return lower_bound(summ,summ+cnt,x)-summ;
}
 
int main (){
    while (~scanf("%d",&n)){
        sum[0]=0;
        for (int i=1; i<=n; i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        sort(sum,sum+n+1);
        cnt=0;
        int tmp=sum[0];
        summ[0]=tmp;
        for (int i=1; i<=n; i++){
            if (sum[i]!=tmp) {
                cnt++;
                summ[cnt]=sum[i];
                tmp=summ[cnt];
            }
        }
        cnt++;
        update(find(0)+1,1);
        int ans;
        tmp=0;
        for (int i=1; i<=n; i++){
            tmp+=a[i];
            ans=getsum(find(tmp)+1);
            update(find(tmp)+1,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值