• ACM学习历程——HDU2227 Find the nondecreasing subsequences(线段树 && dp)


    Description

    How many nondecreasing subsequences can you find in the sequence S = {s1, s2, s3, ...., sn} ? For example, we assume that S = {1, 2, 3}, and you can find seven nondecreasing subsequences, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}.
     

    Input

    The input consists of multiple test cases. Each case begins with a line containing a positive integer n that is the length of the sequence S, the next line contains n integers {s1, s2, s3, ...., sn}, 1 <= n <= 100000, 0 <= si <= 2^31.
     

    Output

    For each test case, output one line containing the number of nondecreasing subsequences you can find from the sequence S, the answer should % 1000000007.
     

    Sample Input

    3
    1 2 3
     

    Sample Output

    7

    这个题目要求的是上升子序列的个数。

    若设sum[i]表示以i为最后一个数的上升子序列的个数。

    首先可以得到的是sum[i] = 1 + ∑(sum[j]) (a[j] <= a[i])。(ps:加1是因为子序列可以只包含一个数)

    但是遍历sum[j]这个操作的时间需要O(n),所以要对这个操作进行优化。

    很容易想到的是区间和,但是这个操作只需要求在i之前比a[i]小的那些点的和。

    于是可以先把所有点的值初始化为0,然后从值最小的那个数开始求解,这样在求小的数的时候,大的数对应的值是0,这样的话大的数的贡献就是0,相当于没有加入计算。

    举例说明:

    对于序列5 1 3 2 4

    先求1这个数,那么sum[1]就是[1, 2]区间内val[i]的和加1,即0+1。此时val[1]更新为1,sum[1]为1。

    再求2这个数,那么sum[2]就是[1, 4]区间内val[i]的和加1,即1+1。此时val[2]更新为2,sum[2]为2。

    再求3这个数,那么sum[3]就是[1, 3]区间内val[i]的和加1,即1+1。此时val[3]更新为2,sum[3]为2。

    再求4这个数,那么sum[4]就是[1, 5]区间内val[i]的和加1,即1+2+2+1。此时val[4]更新为5,sum[4]为6。

    最后求5这个数,那么sum[5]就是[1, 1]区间内val[i]的和加1,即0+1。此时val[5]更新为1,sum[5]为1。

    所以答案就是sum[i]的和,就是12。

    由于可以边求边加,所以sum数组可以省去。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #define LL long long
    #define N 1000000007
    
    using namespace std;
    
    //线段树
    //区间每点增值,求区间和
    const int maxn = 100005;
    struct node
    {
        int lt, rt;
        int val;
    }tree[4*maxn];
    
    //向上更新
    void PushUp(int id)
    {
        tree[id].val = (tree[id<<1].val + tree[id<<1|1].val)%N;
    }
    
    //建立线段树
    void Build(int lt, int rt, int id)
    {
        tree[id].lt = lt;
        tree[id].rt = rt;
        tree[id].val = 0;//每段的初值,根据题目要求
        if (lt == rt)
        {
            //tree[id].val = 1;
            return;
        }
        int mid = (lt + rt) >> 1;
        Build(lt, mid, id<<1);
        Build(mid+1, rt, id<<1|1);
        //PushUp(id);
    }
    
    //增加区间内每个点固定的值
    void Add(int lt, int rt, int id, int pls)
    {
        if (lt <= tree[id].lt && rt >= tree[id].rt)
        {
            tree[id].val += pls * (tree[id].rt-tree[id].lt+1);
            tree[id].val %= N;
            return;
        }
        int mid = (tree[id].lt + tree[id].rt) >> 1;
        if (lt <= mid)
            Add(lt, rt, id<<1, pls);
        if (rt > mid)
            Add(lt, rt, id<<1|1, pls);
        PushUp(id);
    }
    
    //查询某段区间内的he
    LL Query(int lt, int rt, int id)
    {
        if (lt <= tree[id].lt && rt >= tree[id].rt)
            return tree[id].val;
        int mid = (tree[id].lt + tree[id].rt) >> 1;
        LL ans = 0;
        if (lt <= mid)
            ans += Query(lt, rt, id<<1);
        if (rt > mid)
            ans += Query(lt, rt, id<<1|1);
        return ans%N;
    }
    
    struct node1
    {
        LL val;
        int id;
    }p[100005];
    
    bool cmp(node1 a, node1 b)
    {
        if (a.val != b.val)
            return a.val < b.val;
        else
            return a.id < b.id;
    }
    
    int n, len;
    int sum;
    LL ans;
    
    int main()
    {
        //freopen("test.in", "r", stdin);
        while (scanf("%d", &n) != EOF)
        {
            for (int i = 0; i < n; ++i)
            {
                p[i].id = i+1;
                scanf("%I64d", &p[i].val);
            }
            sort(p, p+n, cmp);
            ans = 0;
            Build(1, n, 1);
            for (int i = 0; i < n; ++i)
            {
                sum = Query(1, p[i].id, 1)+1;
                Add(p[i].id, p[i].id, 1, sum);
                ans += sum;
                ans %= N;
            }
            printf("%I64d
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    C# 关于委托和事件的妙文:通过一个例子详细介绍委托和事件的作用;Observer模式简介
    Path.Combine (合并两个路径字符串)方法的一些使用细节
    taskkill /f /im的应用
    powersheel远程连接方法操作
    Centos 定时任务发送smtp邮件
    Centos 发送smtp邮件
    在 Windows 上安装Rabbit MQ 指南
    Quartz.NET总结(五)基于Quartz.net 的开源任务管理平台
    Quartz.NET总结(四)Quartz 远程调度
    Quartz.NET总结(三)Quartz 配置
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/4471061.html
Copyright © 2020-2023  润新知