zoukankan      html  css  js  c++  java
  • 【洛谷】【前缀和+st表】P2629 好消息,坏消息

    【题目描述:】

    uim在公司里面当秘书,现在有n条消息要告知老板。每条消息有一个好坏度,这会影响老板的心情。告知完一条消息后,老板的心情等于之前老板的心情加上这条消息的好坏度。最开始老板的心情是0,一旦老板心情到了0以下就会勃然大怒,炒了uim的鱿鱼。

    uim为了不被炒,知道了了这些消息(已经按时间的发生顺序进行了排列)的好坏度,希望研究如何不让老板发怒。

    uim必须按照时间的发生顺序逐条将消息告知给老板。不过uim可以使用一种叫“倒叙”的手法,例如有n条消息,小a可以从k,k+1,k+2...n,1,2...k-1这种顺序通报。

    他希望知道,有多少个k,从k开始通报到n然后从1通报到k-1可以让老板不发怒。

    【输入格式:】

    第一行一个整数n(1 <= n <= 10^6),表示有n个消息。

    第二行n个整数,按时间顺序给出第i条消息的好坏度Ai(-1000 <= Ai <= 1000)

    【输出格式:】

    一行一个整数,表示可行的方案个数。

    在旁边wzx大(巨)佬的指点下切掉了这道题



    [算法分析:]

    对于要判断区间([l, r])内在某一时刻的和是否为负值,只需要看从([l, r])前缀和序列的值是否为负.

    (这道题是要求对于一个点(i),判断([i, n])([1, i-1])中某一时刻的前缀和是否存在负值)

    那怎样快速地求出这个前缀和序列并判断是否有负值呢?

    枚举的方法显然并不高效,反而和直接模拟的复杂度一样了,抛掉

    判断是否有负值,可以找到一个标准,只要这个标准非负,那整个序列也就没有负值了

    显然的,标准应选为([l, r])前缀和序列的最小值,便可以使用st表或者线段树来快速查询.

    那又应该如何求出([l, r])前缀和序列呢?每次求一遍?显然复杂度会降到约为(O(n^3))

    对于一个序列(a)([1, r])前缀和序列的最小值再减去([1, l - 1])的元素和,即为([l, r])前缀和序列最小值

    即:$$min{sum_{j = l}^{r}sum_{i = l}^{j} a_i} = min{sum_{j = 1}^{r}sum_{i = 1}^{j} a_i} - sum_{i = 1}^{l - 1}a_i$$

    所以当我们根据序列(a)求出([l, r])的前缀和序列sum时,使用st表维护这个序列sum的最小值,

    就可以做到(O(1))查询(min{sum_{j = l}^{r}sum_{i = l}^{j} a_i})

    预处理复杂度(O(nlog_2n)),算法主体的时间复杂度为(O(n))

    而使用线段树也可以维护最小值,根据前缀和序列建树,预处理(O(n)),算法主体复杂度为(O(nlog_2n)),和st表差不多。

    (但是实测好像st表跑的慢一些)

    也可以使用单调队列来求最值,复杂度应该是(O(n)),比st表和线段树都要快,但是我不会QwQ



    ([Code:])

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int MAXN = 1e6 + 1;
    const int K = 20 + 1;
    
    int n;
    int a[MAXN], sum[MAXN];
    int st[MAXN][K], Log2[MAXN];
    
    inline int read() {
        int x=0, f=1; char ch=getchar();
        while(ch<'0' || ch>'9') {
            if(ch == '-') f = -1;
            ch = getchar();
        }
        while(ch>='0' && ch<='9')
            x=(x<<3)+(x<<1)+ch-48, ch=getchar();
        return x * f;
    }
    
    inline int Query(int l, int r) {
        int x = Log2[r - l + 1];
        return min(st[l][x], st[r-(1<<x)+1][x]);
    }
    
    int main() {
        n = read();
        for(int i=1; i<=n; ++i) a[i] = read();
        for(int i=1; i<=n; ++i)
            st[i][0] = sum[i] = sum[i-1] + a[i];
        for(int j=1; j<=K; ++j)
            for(int i=1; i+(1<<j)-1<=n; ++i)
                st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
        for(int i=0; (1<<i)<=n; ++i) Log2[1<<i] = i;
        for(int i=1; i<=n; ++i)
            if(!Log2[i]) Log2[i] = Log2[i-1];
        int ans = 0;
        for(int i=1; i<=n; ++i) {
            if(Query(i, n)-sum[i-1] < 0) continue;
            if(Query(1, i-1) + sum[n] - sum[i-1] >= 0) ++ans;
        }
        printf("%d
    ", ans);
    }
    
  • 相关阅读:
    yii2的安装
    php Laravel windows安装
    vimrc for windows
    Magento学习
    php Ajax 局部刷新
    php用于URL的base64
    分享自建的 Jrebel License Server 激活 Jrebel
    Windows下安装Redis服务(zip)
    Windows下安装Redis服务
    MySQL中有关TIMESTAMP和DATETIME的总结
  • 原文地址:https://www.cnblogs.com/devilk-sjj/p/9078732.html
Copyright © 2011-2022 走看看