洛谷P1637 三元上升子序列

Posted zbtrs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷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\n", ans);

    return 0;
}

 

以上是关于洛谷P1637 三元上升子序列的主要内容,如果未能解决你的问题,请参考以下文章

P1637 三元上升子序列

P1637 三元上升子序列

P1637 三元上升子序列

Luogu P1637 三元上升子序列

Luogu P1637 三元上升子序列权值线段树By cellur925

『Luogu 1637』三元上升子序列 (树状数组)