首页 > 代码库 > HDU--2227--Find the nondecreasing subsequences--线段树

HDU--2227--Find the nondecreasing subsequences--线段树

Find the nondecreasing subsequences

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1393    Accepted Submission(s): 494


Problem 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
 
题意就是求数列的非递减子序列数量

当你像我一样干了它6个多小时还没AC的时候,心都哇凉了
很容易想到,每输入一个元素,在它之前输入的元素“们”的并且子序列的末尾元素比它小的子序列都可以跟它组成新的子序列
如:1,2,3在输入最后一个3的时候{1},{1,2},{2}都可以跟2组成新序列{1,3}{1,2,3}{2,3},别忘了,3自己还可以弄个新的出来{3}

我看了几乎所有百度得到的博客,不是树桩子就是那种前后比较的压缩型离散线段树,其实,大家都想要一个给力的测试数据,说的没错吧—。—!       那就直接测试50个1,然后100个1,你会发现数字多了之后,小小的差异会变的巨大,就像你们看我博客一次给我一块钱,13亿人都来看...不扯了,嘿嘿
在离散方面出了问题的主要去看看排序是否出错,下面也有点我自己的心得!!

#include <iostream>
#include <cstdio>
#include <algorithm>
#define MM 1000000007
#define Max 100010
using namespace std;
struct node
{
    int l,r,s;	//s:子序列的数量
}tree[Max*6];
struct node1
{
    int a,b;	//a:输入序列的值,b:输入的序列的位置
}sd[Max];
int ss[Max];	//离散化之后的序列
void setit(int l,int r,int d)	//建立,这个不用讲了—。—!
{
    tree[d].l=l;
    tree[d].r=r;
    tree[d].s=0;
    if(l==r)return;
    setit(l,(l+r)/2,d*2);
    setit((l+r)/2+1,r,d*2+1);
}
void insert(int e,int k,int d)	//插入
{
    int l,r,mid;
    l=tree[d].l;
    r=tree[d].r;
    mid=(l+r)/2;
    tree[d].s=(tree[d].s+k)%MM;	//一路遍历下去的同时在这个区间段[l,r]中加k
    if(l==r)return;	//遍历到树叶了就结束
    if(e>mid)insert(e,k,d*2+1);
    else insert(e,k,d*2);
}
int find(int e,int d)
{
    int l,r,mid;
    l=tree[d].l;
    r=tree[d].r;
    mid=(l+r)/2;
    if(e>=r)return tree[d].s;	//如果区间段的右值小于等于e,即确定次区间段类的数字都小于e,就直接返回子序列数
    if(e>mid)return (tree[d*2].s+find(e,d*2+1))%MM;	//e点在区间段的右端时,左子树的子序列数+继续遍历右子树
    else return find(e,d*2);
}
bool cmp(const node1 &a,const node1 &b)
{
    return a.a<b.a||a.a==b.a&&a.b<b.b;	//这里是重点,标记####,下边会讲
}
int main (void)
{
    int n,i,j,k,l,sum;
    while(scanf("%d",&n)!=EOF)
    {
        sum=0;
        setit(0,n,1);
        for(i=0;i<n;i++)
        {
            scanf("%d",&sd[i].a);
            sd[i].b=i;	//标记位置
        }
        sort(sd,sd+n,cmp);	//排序
        for(i=0;i<n;i++)
        ss[sd[i].b]=i;	//把原序列离散成新序列,即把元素弄成1~n,因为元素最大是2^31,数组开不了这么大
        for(i=0;i<n;i++)
        {
            k=(find(ss[i],1)+1)%MM;	//查询可以与当前元素形成新序列的子序列的集合,还有元素自己也可以,所以+1
            sum=(sum+k)%MM;
            insert(ss[i],k,1);	//插入,这里把k插入是因为{1}和{1,3}是不同的,都可以用来和4组成新子序列(假设n=4)
        }
        printf("%d\n",sum);
    }
    return 0;
}
上边的标记

####   sort排序不能很好地处理相同元素的排序,比如我输入50个1,结果后25个1整整齐齐去了前25个1的前面,这样就破坏了原序列的顺序,我这个离散化是1-n的直接标记,比如1,1,7,7会变为1,2,3,4,而还有一种是压缩性的,会变为1,1,2,2,把相同的也保存相同的,这两个有一个会错,原因就是这里讲的sort的缺陷,所以加一个||a.a==b.a&&a.b<b.b意思是在排序的同时,相同元素不会打乱原先的顺序