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

      我一开始想的是O(n2)的做法,就是暴力dp。

      考虑对于一个位置 i ,如果说存在(1 <= j < i ),使得sum[ i ] >= sum [ j ] ,那么 j 到 i 就可以分为一组,i 的方案数就加上 j 的方案数。这是O(n2)的,对于100000这样的数据显然过不了。

      考虑优化:

      我在想这个问题的时候,感觉,要用 logn 的时间求出存在(1 <= j < i ),使得sum[ i ] >= sum [ j ]的dp[ j ] 的和常规想法几乎是不可能的……所以肯定要维护一个数据结构。

      我一开始想的是平衡树,旋转到根后,维护儿子的和(dp和),这样在找到sum [ j ] 的时候,加上左儿子的和就可以了,但是我并不想写平衡树……

      然后我就想,如果说sum值是有序的,那么 “ 如果说存在(1 <= j < i ),使得sum[ i ] > sum [ j ] ” 这个问题就可以用线段树维护了。就会发现这个求sum顺序对的问题,就是二位偏序而已。那么思路就有了:sum排序离散化,对于现在的 i ,在线段树sum [ i ] 位置上加上【1,sum [ i ] 】的和。这样既满足 i 有序,也满足加上了所有sum[ i ] > = sum [ j ]的 dp [ j ] 的值。

      温馨概括:线段树上第 i 位代表排名为第 i 小的 sum 的 dp 值。

      我在写的时候犯了几个错误……

      第一个,就是写线段树求和的时候,比如应该在左区间找对应区间,我到左区间后把对应区间减半了……

      第二个,也比较重要。最后的答案是第 n 次求和的值,而不是最后求sum [ n ] 位置的和。我想了半天……才意识到自己太傻了,首先dp [ n ] 的值本来就是第 n 次求和的那个值;再说,我在对应位置加上这个值之前,这个位置很有可能都有值了啊,再求不就多了嘛!说得严谨点:每一个dp [ i ] 只被ans记录一遍,我们再后来并不能求出任何一个单独的dp [ i ] 。

      所以上面的概括严禁点就是:

      线段树上第 i 位代表排名为第 i 小的 多个sum 的 dp 值之和。

      我感觉这道题挺不错的(好想我感觉每道题都挺不错……)

      代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<cstdlib>
    using namespace std;
    #define maxn 5000005
    #define ls now<<1
    #define rs now<<1|1
    #define int long long
    #define mod 1000000009
    int sum[maxn],l[maxn],r[maxn];
    int dp[maxn],f[maxn],a[maxn],p[maxn],pre[maxn];
    int n,T;
    int find(int x)
    {
        int ans=0,L=1,R=T;
        while(L<=R)
        {
            int mid=(L+R)>>1;
            if(p[mid]>=x)
            {
                ans=mid;
                R=mid-1;
            }
            else L=mid+1;
        }
        return ans;
    }
    void up(int now)
    {
        f[now]=(f[ls]+f[rs])%mod;
    }
    void build(int now,int L,int R)
    {
        l[now]=L;
        r[now]=R;
        if(L==R) return ;
        int mid=(L+R)>>1;
        build(ls,L,mid);
        build(rs,mid+1,R);
    }
    int query(int now,int L,int R)
    {
        if(l[now]==L&&r[now]==R)
            return f[now];
        int mid=(l[now]+r[now])>>1;
        if(R<=mid) return query(ls,L,R);
        else if(L>mid) return query(rs,L,R);
        else return (query(ls,L,mid)+query(rs,mid+1,R))%mod;
    }
    void update(int now,int id,int x)
    {
        if(l[now]==r[now]&&l[now]==id)
        {
            (f[now]+=x)%=mod;
            return ;
        }
        int mid=(l[now]+r[now])>>1;
        if(id<=mid) update(ls,id,x);
        else update(rs,id,x);
        up(now);
    }
    main()
    {
        scanf("%lld",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
            pre[i]=sum[i];
        }
        sort(sum+1,sum+n+1);
        T=unique(sum+1,sum+n+1)-sum-1;
        for(int i=1; i<=T; i++)
            p[i]=sum[i];
        build(1,1,T);
        int ans;
        for(int i=1; i<=n; i++)
        {
            ans=query(1,1,find(pre[i]))%mod;// 1是dp[0] 
            if(pre[i]>=0) ans++;
            update(1,find(pre[i]),ans);
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    Java String字符串深入详解
    每日linux命令学习-sed
    每日linux命令学习-历史指令查询(history、fc、alias)
    每日linux命令学习-rpm命令
    每日linux命令学习-head命令和tail命令
    每日linux命令学习-lsattr和chattr
    每日linux命令学习-xargs命令
    每日linux命令学习-read命令
    测试mysql性能工具
    mysql 免安装版文件含义及作用
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/10924071.html
Copyright © 2011-2022 走看看