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\n", ans);
    }
    return 0;
}
时间: 2024-08-03 02:40:56

ACM学习历程——HDU2227 Find the nondecreasing subsequences(线段树 && dp)的相关文章

ACM学习历程—HDU5475 An easy problem(线段树)(2015上海网赛08题)

Problem Description One day, a useless calculator was being built by Kuros. Let's assume that number X is showed on the screen of calculator. At first, X = 1. This calculator only supports two types of operation. 1. multiply X with a number. 2. divid

ACM学习历程——NOJ1113 Game I(贪心 || 线段树)

Description 尼克发明了这样一个游戏:在一个坐标轴上,有一些圆,这些圆的圆心都在x轴上,现在给定一个x轴上的点,保证该点没有在这些圆内(以及圆上),尼克可以以这个点为圆心做任意大小的圆,他想知道自己做多可以与多少个给定的圆相交(相切也算,包含不算). Input 输入有多组数据 输入到文件尾 每一组数据有一个整数n(1<=n<=100000),表示总共有n个圆. 接下是n行,每行两个整数xi,ri表示该圆的圆心坐标和半径. 接下来一行为一个整数x,表示尼克选取点的位置. x xi的范

ACM学习历程—Hihocoder 1289 403 Forbidden(字典树 || (离线 &amp;&amp; 排序 &amp;&amp; 染色))

http://hihocoder.com/problemset/problem/1289 这题是这次微软笔试的第二题,过的人比第三题少一点,这题一眼看过去就是字符串匹配问题,应该可以使用字典树解决.不过当时还有一个想法就是离线处理,把所有查询进行排序,然后用rule去匹配查询,进行染色处理,而且每个查询只进行一次染色.事实证明,如果比赛的时候采用第二种方法应该能拿全分,但是我用了第一种方法,导致只拿了10分...因为我没有考虑同一个rule出现两次的情况,但是字典树中会直接被后面的rule覆盖,

ACM学习历程—CodeForces 176B Word Cut(字符串匹配 &amp;&amp; dp &amp;&amp; 递推)

Description Let's consider one interesting word game. In this game you should transform one word into another through special operations. Let's say we have word w, let's split this word into two non-empty parts x and y so, that w = xy. A split operat

ACM学习历程—HDU 4726 Kia&#39;s Calculation( 贪心&amp;&amp;计数排序)

DescriptionDoctor Ghee is teaching Kia how to calculate the sum of two integers. But Kia is so careless and alway forget to carry a number when the sum of two digits exceeds 9. For example, when she calculates 4567+5789, she will get 9246, and for 12

ACM学习历程—HDU 5023 A Corrupt Mayor&#39;s Performance Art(广州赛区网赛)(线段树)

Problem Description Corrupt governors always find ways to get dirty money. Paint something, then sell the worthless painting at a high price to someone who wants to bribe him/her on an auction, this seemed a safe way for mayor X to make money. Becaus

ACM学习历程—UESTC 1226 Huatuo&#39;s Medicine(数学)(2015CCPC L)

题目链接:http://acm.uestc.edu.cn/#/problem/show/1226 题目就是构造一个对称的串,除了中间的那个只有1个,其余的两边都是对称的两个,自然答案就是2*n-1. 代码: #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <algorithm> #

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

ACM学习历程—HDU5696 区间的价值(分治 &amp;&amp; RMQ &amp;&amp; 线段树 &amp;&amp; 动态规划)

http://acm.hdu.edu.cn/showproblem.php?pid=5696 这是这次百度之星初赛2B的第一题,但是由于正好打省赛,于是便错过了.加上2A的时候差了一题,当时有思路,但是代码就是过不去..这次应该是无缘复赛了.. 先不水了,省赛回来,我看了一下这个题,当时有个类似于快排的想法,今天试了一下,勉强AC了..跑了3S多. 思路就是我枚举区间左值lt,那么[lt, n]区间内最值的角标分别为mi和ma.于是设to = max(mi, ma).也就是说在to右侧的所有区间