三元上升子序列

LuoguP1637

这道题我看了两种做法

一:

对于每个位置分别求出前面小于它的数的个数和后面大于它的数的个数,最后根据乘法原理相乘求和即可

二:

这个做法挺厉害的,可以扩展到M元上升子序列

就还是DP求最长上升子序列个数,不过要用树状数组优化,具体的在代码里有解释

Code1:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 3e5 + 7;
 4 int n;
 5 int c[N], a[N], b[N], lit[N], big[N];
 6 void add(int x, int v) {
 7     for (int i = x; i <= n; i += i & -i) {
 8         c[i] += v;
 9     }
10 }
11 int ask(int x) {
12     int re = 0;
13     for (int i = x; i > 0; i -= i & -i) {
14         re += c[i];
15     }
16     return re;
17 }
18 int main () {
19     scanf("%d", &n);
20     for (int i = 1; i <= n; i++) {
21         scanf("%d", &a[i]);
22         b[i] = a[i];
23     }
24     sort(b + 1, b + 1 + n);
25     int m = unique(b + 1, b + 1 + n) - (b + 1);
26     for (int i = 1; i <= n; i++) {
27         a[i] = lower_bound(b + 1, b + 1 + m, a[i]) - b;
28     }
29     for (int i = 1; i <= n; i++) {
30         add(a[i], 1);
31         lit[i] = ask(a[i] - 1);
32     }
33     memset(c, 0, sizeof c);
34     for (int i = n; i >= 1; i--) {
35         add(a[i], 1);
36         big[i] = n - i + 1 - ask(a[i]);
37         //用n-i这个区间(也就是i之后)的长度减去这之中比a[i]小的就是比a[i]大的 
38     }
39     long long ans = 0;
40     for (int i = 1; i <= n; i++) {
41         ans += lit[i] * big[i];
42     }
43     printf("%lld
", ans);
44     return 0;
45 }
View Code

Code2:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 3e5 + 7;
 4 int n;
 5 int c[N], a[N], b[N], f[4][N];
 6 //f[i][j]表示长度为i以a[j]为结尾的上升子序列个数 
 7 void add(int x, int v) {
 8     for (int i = x; i <= n; i += i & -i) {
 9         c[i] += v;
10     }
11 }
12 int ask(int x) {
13     int re = 0;
14     for (int i = x; i > 0; i -= i & -i) {
15         re += c[i];
16     }
17     return re;
18 }
19 int main () {
20     scanf("%d", &n);
21     for (int i = 1; i <= n; i++) {
22         scanf("%d", &a[i]);
23         b[i] = a[i];
24     }
25     sort(b + 1, b + 1 + n);
26     int m = unique(b + 1, b + 1 + n) - (b + 1);
27     for (int i = 1; i <= n; i++) {
28         f[1][i] = 1;//初始化一下 
29         a[i] = lower_bound(b + 1, b + 1 + m, a[i]) - b;
30     }
31     for (int i = 2; i <= 3; i++) {//本题只需要长度为3 
32         memset(c, 0, sizeof c);//每个长度都要新的树状数组 
33         for (int j = 1; j <= n; j++) {
34             f[i][j] = ask(a[j] - 1);//当前的子序列就是比a[j]小的数 
35             add(a[j], f[i - 1][j]);//比a[j]大的位置加上不选a[j]时的子序列个数 
36         }
37     }
38     long long ans = 0;
39     for (int i = 1; i <= n; i++) {
40         ans += f[3][i];
41     }
42     printf("%lld
", ans);
43     return 0;
44 }
View Code
原文地址:https://www.cnblogs.com/Sundial/p/11830601.html