zoukankan      html  css  js  c++  java
  • 洛谷P1637 三元上升子序列

    P1637 三元上升子序列

    •  
    • 48通过
    • 225提交
    • 题目提供者该用户不存在
    • 标签云端
    • 难度提高+/省选-
    • 时空限制1s / 128MB

     提交  讨论  题解  

    最新讨论更多讨论

    • 为什么超时啊
    • a的数据比较大啊,真的能用…

    题目描述

    Erwin最近对一种叫"thair"的东西巨感兴趣。。。

    在含有n个整数的序列a1,a2......an中,

    三个数被称作"thair"当且仅当i<j<k且ai<aj<ak

    求一个序列中"thair"的个数。

    输入输出格式

    输入格式:

    开始一个正整数n,

    以后n个数a1~an。

    输出格式:

    "thair"的个数

    输入输出样例

    输入样例#1
     

    Input

    4

    2 1 3 4

    Output

    2

    Input

    5

    1 2 2 3 4

    Output

    7

    对样例2的说明:

    7个"thair"分别是

    1 2 3

    1 2 4

    1 2 3

    1 2 4

    1 3 4

    2 3 4

    2 3 4

    输出样例#1

    说明

    约定 30%的数据n<=100

    60%的数据n<=2000

    100%的数据n<=30000

    大数据随机生成

    0<=a[i]<=maxlongint

    分析:这道题可以借鉴之前求逆序对那样求,我们只需要求对于每一个数i,在i之前比i小的数的个数和在i之后比i大的数的个数,相乘起来,最后将所有结果加起来就是答案,关键就是如何求出这些数的个数。可以利用树状数组。先将数据离散化,先找小于i的,因为要严格小于,所以我们要先-1再查找,然后添加进去,然后找大于i的,从后往前枚举,相当于我们找到i之后不大于i的个数,然后用n-i(i之后的数的个数)减去结果就是严格大于i的数的个数,最后统计一下答案即可。

         如果将这道题推广到要找k个数的情况,我们需要用到dp,则dp[i][j] = dp[k][j-1] + dp[k][j],其中k为小于i中最靠后的一个数.

    #include <iostream>  
    #include <cstdlib>  
    #include <cstdio>  
    #include <cstring>  
    #include <string>  
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <cmath>
    
    using namespace std;
    
    long long d1[30010], d2[30010],ans1[30010],ans2[30010],dis[30010],ans;
    
    struct node
    {
        long long v;
        int id;
    }a[30010];
    
    int n;
    
    void update(long long x, int v)
    {
        while (x <= n)
        {
            d1[x] += v;
            x += x &(-x);
        }
    }
    
    int sum(long long x)
    {
        int cnt = 0;
        while (x)
        {
            cnt += d1[x];
            x -= x & (-x);
        }
        return cnt;
    }
    
    void update2(long long x, int v)
    {
        while (x <= n)
        {
            d2[x] += v;
            x += x &(-x);
        }
    }
    
    int sum2(long long x)
    {
        int cnt = 0;
        while (x)
        {
            cnt += d2[x];
            x -= x & (-x);
        }
        return cnt;
    }
    
    bool cmp(node a, node b)
    {
        return a.v < b.v;
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%lld", &a[i].v);
            a[i].id = i;
        }
        sort(a + 1, a + 1 + n, cmp);
        dis[a[1].id] = 1;
        int tot = 1;
        for (int i = 2; i <= n; i++)
        {
            if (a[i].v != a[i - 1].v)
                tot++;
            dis[a[i].id] = tot;
        }
        for (int i = 1; i <= n; i++)
        {
            ans1[i] = sum(dis[i] - 1); //是找严格小于的而不是小于等于的
            update(dis[i], 1);
        }
        for (int i = n; i >= 1; i--)
        {
            ans2[i] = n - i - sum2(dis[i]);
            update2(dis[i], 1);
        }
        for (int i = 1; i <= n; i++)
            ans += ans1[i] * ans2[i];
        printf("%lld
    ", ans);
    
        return 0;
    }
  • 相关阅读:
    java学习day02---Spring Boot综合运用---活动模块
    java学习day01---GC
    课程学习总结报告
    结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
    深入理解系统调用
    基于mykernel 2.0编写一个操作系统内核
    超码 候选码 主码 替换码 数据库 定义
    如何评测软件工程知识技能水平?
    创新产品的需求分析:未来的图书会是什么样子?
    案例分析:设计模式与代码的结构特性(桥接模式)
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7078292.html
Copyright © 2011-2022 走看看