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; }