zoukankan      html  css  js  c++  java
  • 奶牛抗议

    奶牛抗议

    描述

    约翰家的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

    提示

    说明 
    解释:如果分两组,可以把前三头分在一组,或把后三头分在一组; 
    如果分三组,可以把中间两头分在一组,第一和最后一头奶牛自成一组;最后一种分法是把四头奶牛分在同一组里。

    思路:

    在做这道题之前,我们不妨先来想想这道题暴力怎么做。于是我们很容易得到一个简单的暴力转移方程:

    令f[i]表示到i的方案数,有f[i]=∑f[j],k=j+1∑i a[k]≥0
    写成前缀和的形式便是f[i]=∑f[j],sum[i]-sum[j]≥0,于是

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define mod 1000000009 
     4 using namespace std;
     5 ll n,sum[100005],ans,f[100005],a[100005];
     6 int main()
     7 {
     8     scanf("%lld",&n);
     9     for(ll i=1;i<=n;i++)
    10     {
    11         scanf("%lld",&a[i]);
    12         sum[i]=sum[i-1]+a[i];
    13     }
    14     f[0]=1;
    15     for(ll i=1;i<=n;i++)
    16         for(ll j=0;j<i;j++)
    17             if(sum[i]-sum[j]>=0)
    18                 f[i]=(f[i]+f[j])%mod;
    19     printf("%lld
    ",f[n]);
    20     return 0;
    21 }

    可是这是n^2,怎么优化?

    我们再来仔细看看这个代码,注意到这一句话:sum[i]-sum[j]>=0,我们将其转化一下是不是可以变成sum[i]>=sum[j]呢?

    这时候我们发现相当于求在i前面并且前缀和小于sum[i]的所有f[i]和,这就可以用一个树状数组优化了,在树状数组维护下标为sum[i],f[i]的前缀和。对于每个f[i]即为树状数组

    上sum[i]的前缀和。

    这里需要注意的是前缀和可能为负,而树状数组下标不能为负,所以我们要离散化一下。于是,上代码:

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define mod 1000000009
     4 #define lowbit(x) x&(-x)
     5 using namespace std;
     6 ll sum[1000001],n,f[1000001],b[1000001],num,a[1000001];
     7 struct data {
     8     ll v,id;
     9 } t[1000001];
    10 bool cmp(data x,data y) {
    11     if(x.v==y.v)
    12         return x.id<y.id;
    13     else
    14         return x.v<y.v;
    15 }
    16 void add(ll x,ll val) {
    17     while(x<=n+1) {
    18         sum[x]+=val;
    19         x+=lowbit(x);
    20     }
    21 }
    22 ll ask(ll x) {
    23     ll ans=0;
    24     while(x) {
    25         ans+=sum[x];
    26         x-=lowbit(x);
    27     }
    28     return ans;
    29 }
    30 int main() {
    31     scanf("%lld",&n);
    32     for(ll i=1; i<=n; i++) {
    33         scanf("%lld",&a[i]);
    34         f[i+1]=f[i]+a[i];
    35     }
    36     f[1]=0;
    37     for(ll i=1; i<=n+1; i++) {
    38         t[i].v=f[i];
    39         t[i].id=i;
    40     }
    41     sort(t+1,t+n+2,cmp);
    42     for(ll i=1; i<=n+1; i++)
    43         b[t[i].id]=i;//离散化
    44     add(b[1],1);
    45     for(ll i=2; i<=n+1; i++) {
    46         num=ask(b[i])%mod;
    47         add(b[i],num);
    48     }
    49     printf("%lld
    ",num%mod);
    50     return 0;
    51 }

    值得注意的是,我在这里把前0个数前缀和为零的情况也插入到了树状数组,所以注意一下小细节。

    如果有什么不对的地方欢迎各位巨佬指正。

    撒花~~~

  • 相关阅读:
    二阶段任务分配
    二阶段12.2
    针对提出的意见的改进
    一阶段spring(小呆呆)团队评分
    搜狗输入法使用感受
    省呱呱典型用户和用户场景
    省呱呱意见评论
    11/21
    11/20小组计划
    11/19小组计划
  • 原文地址:https://www.cnblogs.com/sbwll/p/13196215.html
Copyright © 2011-2022 走看看