码迷,mamicode.com
首页 > 其他好文 > 详细

奶牛抗议

时间:2020-06-26 14:35:33      阅读:48      评论:0      收藏:0      [点我收藏+]

标签:des   blog   print   ++   优化   lin   nbsp   main   def   

题目

Description

约翰家的N 头奶牛正在排队游行抗议。一些奶牛情绪激动,约翰测算下来,排在第i 位的奶牛的理智度为Ai,数字 
可正可负。约翰希望奶牛在抗议时保持理性,为此,他打算将这条队伍分割成几个小组,每个抗议小组的理智度之 
和必须大于或等于零。奶牛的队伍已经固定了前后顺序,所以不能交换它们的位置,所以分在一个小组里的奶牛必 
须是连续位置的。除此之外,分组多少组,每组分多少奶牛,都没有限制。约翰想知道有多少种分组的方案,由于 
答案可能很大,只要输出答案除以1000000009 的余数即可。 

Input

第一行:单个整数N,1 ≤ N ≤ 100000 
第二行到第N + 1 行:第i + 1 行有一个整数Ai,10^5 ≤ Ai ≤ 10^5 

Output

单个整数:表示分组方案数模1000000009 的余数 

Sample Input

4
2
3
-3
1

Sample Output

4

Hint

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


 

思路:

如果不知道怎么求a前面小于a的个数

https://www.cnblogs.com/wzx-RS-STHN/p/13193271.html

暴力

首先我们先把这一题的dp思路想清;

dp[i]代表1-i的分组方案数;s[i]代表1-i的和;

如果j-i区间的和大于等于 0;dp[i]+=dp[j];

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,ha=1; char c=getchar();
    while (c<0||c>9) {if (c==-) ha=-1; c=getchar();}
    while (c>=0&&c<=9) {a=a*10+c-0; c=getchar();}
    return a*ha;
}
const ll N=5*1e5;
const ll mod=1000000009;
ll s[N],ans,n,a[N],dp[N];
int main()
{
    n=read();
    for(ll i=1;i<=n;i++)
    {
        a[i]=read();
        s[i]=s[i-1]+a[i];
    }
    dp[0]=1;
    for(ll i=1;i<=n;i++)
    for(ll j=0;j<i;j++)
    {
        if(s[i]>=s[j])//相当if(s[i]-s[j]>=0) 也就是看这一段和是否>=0 
            dp[i]=(dp[i]+dp[j])%mod;
    }
    printf("%lld\n",dp[n]);
}

 

正解

那么怎么优化呢?树状数组!!!

转移方程

    for(ll j=0;j<i;j++)
    {
        if(s[i]>=s[j])//相当if(s[i]-s[j]>=0) 也就是看这一段和是否>=0 
            dp[i]=(dp[i]+dp[j])%mod;
    }

容易想到,这一段代码其实相当于求,s[i]前面比s[i]小的s[j]有多少个;dp[i]+=dp[j];

 

    ll x=findout(b[i]);//找出前面有多少数比b[i]小,并把那些位置上的方案数加上; 
        ans=x%mod;
        insert(b[i],ans);//将ans加入树状数组相当于暴力代码的记录dp[i] 

 

b[i]是s[i]的离散化;

通过树状数组优化转移方程就ok了;

 

代码:

 

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline ll read()
{
    ll a=0,ha=1; char c=getchar();
    while (c<0||c>9) {if (c==-) ha=-1; c=getchar();}
    while (c>=0&&c<=9) {a=a*10+c-0; c=getchar();}
    return a*ha;
}
const ll mod=1000000009;
ll n,m,sum[1000010];
ll b[1000010],a1[1000010],s[1000010];
struct oh
{
    ll v,num;
}a[1000010];
inline ll lowbit(ll x)//树状数组基本操作 
{
    return x&(-x);
}
inline void insert(ll x,ll y)
{
    while(x<=n+1)
    {
        sum[x]+=y;
        x+=lowbit(x);
    }
}
inline ll findout(ll x)
{
    ll ans=0;
    while(x)
    {
        ans+=sum[x];
        x-=lowbit(x);
    }
    return ans;
}
inline ll cmp(oh a,oh b)
{
    if(a.v==b.v)
        return a.num<b.num;
    else
        return a.v<b.v;
}
int main()
{
    n=read();
    for(ll i=1;i<=n;i++)
    {
        a1[i]=read();
        s[i+1]=s[i]+a1[i];//记录前缀和 
    }
    s[1]=0;//边界相当于dp[0]=1 
    for(ll i=1;i<=n+1;i++)
    {
        a[i].v=s[i];
        a[i].num=i;
    }//离散化 
    sort(a+1,a+n+2,cmp);
    for(ll i=1;i<=n+1;i++)
        b[a[i].num]=i;
    //代码修改艰难痕迹 
//    cout<<endl;
//    for(ll i=1;i<=n+1;i++)
//        cout<<b[i]<<endl;
    insert(b[1],1);
    ll ans=0;
    for(ll i=2;i<=n+1;i++)
    {
        ll x=findout(b[i]);//找出前面有多少数比b[i]小,并把那些位置上的方案数加上; 
        ans=x%mod;
        insert(b[i],ans);//将ans加入树状数组相当于暴力代码的记录dp[i] 
    }
    printf("%lld\n",ans);
}

 

 

 

//相当if(s[i]-s[j]>=0) 也就是看这一段和是否>=0 

奶牛抗议

标签:des   blog   print   ++   优化   lin   nbsp   main   def   

原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13194555.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!