fzu-oj 2236 第十四个目标-树状数组

链接:http://acm.fzu.edu.cn/problem.php?pid=2236

Problem Description

目暮警官、妃英里、阿笠博士等人接连遭到不明身份之人的暗算,柯南追踪伤害阿笠博士的凶手,根据几起案件现场留下的线索发现凶手按照扑克牌的顺序行凶。在经过一系列的推理后,柯南发现受害者的名字均包含扑克牌的数值,且扑克牌的大小是严格递增的,此外遇害者与毛利小五郎有关。

为了避免下一个遇害者的出现,柯南将可能遭到暗算的人中的数字按关联程度排列了出来,即顺序不可改变。柯南需要知道共有多少种可能结果,满足受害人名字出现的数字严格递增,但是他柯南要找出关键的证据所在,所以这个任务就交给你了。

(如果你看不懂上面在说什么,这题是求一个数列中严格递增子序列的个数。比如数列(1,3,2)的严格递增子序列有(1)、(3)、(2)、(1,3)、(1,2),共5个。长得一样的但是位置不同的算不同的子序列,比如数列(3,3)的答案是2。)

Input

多组数据(<=10),处理到EOF。

第一行输入正整数N(N≤100 000),表示共有N个人。

第二行共有N个整数Ai(1≤Ai≤10^9),表示第i个人名字中的数字。

Output

每组数据输出一个整数,表示所有可能的结果。由于结果可能较大,对1 000 000 007取模后输出。

Sample Input

3
1 3 2

Sample Output

5

Source

福州大学第十三届程序设计竞赛

思路:

首先:本题如果用动态规划会操时o(n^2)
本题对树状数组的应用是实现动态规划,树状数组c中保存的是第i小的数出现了几次
举例说吧:设输入的数是7 3 4 7 9,首先把给定的数按从小到大排列,然后去重,保存在b数组之中,所以b数组是3 4 7 9,
然后检索原来的数组(a数组)中的每一个元素(比如7吧),就去b数组中发现他是第3小的,他前面有两个数,遍历树状数去求和,
就是求出了以3和4这两个元素结尾的字符串的总数,然后更新树状数组,以此类推

动态规划代码(会超时):


#include<iostream>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<deque>
#include<vector>


#define ll long long
#define INF 0x3f3f3f3f
#define maxn  100005
#define MOD 1000000007



#include <cstdio>
#include <algorithm>
#define mod 1000000007
using namespace std;
long long num[100010], dp[100010];
int main()
{
    int n;

    while ( ~scanf( "%d", &n)){
        for ( int i = 1; i <= n; i++){
            scanf( "%d", &num[i]);
        }
        long long s = 0;
        for(int i = 1;i <= n;i++){
            dp[i] = 1;
            for(int j = 1;j < i;++j){
                if(num[j] < num[i]){
                   dp[i] += dp[j];
                }
            }
        }
        for(int i = 1;i <= n;i++){
             s += dp[i];
             s = s % mod;
        }
        printf( "%lld\n", s);
    }
    return 0;
}

AC代码:


#include<iostream>
#include<string>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<deque>
#include<vector>

#define ll long long
#define INF 0x3f3f3f3f
#define maxn  100005
#define MOD 1000000007

using namespace std;

int  c[maxn];
int a[maxn];
int b[maxn];
int dp[maxn];

int n;
int cnt;
int Query(int i)
{
    int ans = 0;
    while(i >= 1)
    {
        ans = (ans + c[i]) % MOD;
        i -= i & (-i);
    }
    return ans;
}

void Update(int i, int val)
{
    while(i <= cnt)
    {
        c[i] = (c[i] + val) % MOD;
        i += i&(-i);
    }
}


int main()
{
    while(~scanf("%d",&n)){
        for(int i = 1;i <= n;i++){
            scanf("%d",&a[i]);
            b[i] = a[i];
        }
        sort(b+1,b+1+n);
        memset(c,0,sizeof(c));
        memset(dp,0,sizeof(dp));
        cnt = 1;
        for(int i = 2;i <= n;i++){
            if(b[i] != b[i-1]){
                b[++cnt] = b[i];
            }
        }
        for(int i = 1;i <= n;i++){
            int pos = lower_bound(b+1,b+cnt+1,a[i])- b;
            dp[i] = Query(pos-1)+1%MOD;
            Update(pos,dp[i]);
        }
        cout << Query(cnt) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值