第一次写绿题解题报告,多少有点膈应quq
反正这个也是主席树和树套树的前置操作,就当是水博客了!
反正整个五月也没写几个博客
洛谷传送门
最近洛谷出了个一键复制 markdown 的功能,这下方便多了!
Erwin 最近对一种叫 thair
的东西巨感兴趣。。。
在含有 \(n\) 个整数的序列 \(a_1,a_2,\ldots,a_n\) 中,三个数被称作thair
当且仅当 \(i<j<k\) 且 \(a_i<a_j<a_k\)。
求一个序列中 thair
的个数。
开始一行一个正整数 \(n\),
以后一行 \(n\) 个整数 \(a_1,a_2,\ldots,a_n\)。
一行一个整数表示 thair
的个数。
根据乘法原理,不难发现,一个的 thair
就等于比它小且在前面的数个数乘上在它后面且比它大的数的个数。
比如对于样例:
数字 \(3\) 的 thair
就等于 \(2\times1=2\)。
因此,我们可以维护一个权值线段树,每次插入一个数,并记录下它的 thair
,最后计算结果即可。
简单科普一下什么是权值线段树。
对一个数列 \(a\) 构造数列 \(b\) ,其中 \(b_i\) 表示 \(i\) 的出现次数,那么 \(b\) 就是 \(a\) 的权值数列。(其实就和桶排序的桶差不多)
对权值数列构造线段树,得到的就是权值线段树了。
考虑到要记录两个数(一个大于一个小于),我们需要建立两个权值线段树,当然也可以记录完一个数据之后清空线段树。
首先肯定要先做一个离散化,不然值域太大没法直接开权值线段树。
void get_hash() { memcpy(t, a, sizeof(t));//开一个临时数组放排序加去重 sort(t + 1, t + n + 1); int* end = unique(t + 1, t + n + 1); for (int i = 1; i <= n; i++) a[i] = lower_bound(t + 1, end, a[i]) - t;//一个个放进原数组里面 }
接下来是权值线段树的查询和修改,方法和普通线段树大同小异。
int query(int id, int l, int r, int x, int y) { if (x <= l && r <= y) return s[id]; int mid = (l + r) >> 1, res = 0; if (mid >= x) res += query(id * 2, l, mid, x, y); if (mid < y) res += query(id * 2 + 1, mid + 1, r, x, y); return res; } void modify(int id, int l, int r, int v) { if (l == r && l == v) return s[id]++, (void)Flannelette;//做一个简写 int mid = (l + r) >> 1; if (mid >= v) modify(id * 2, l, mid, v); else modify(id * 2 + 1, mid + 1, r, v); pushup(id); }
最后是主函数的查询和修改,就放到完整代码里面了。
完整代码如下:
#include <algorithm> #include <cctype> #include <cstdio> #include <cstring> #define int long long #define Flannelette 0x00000000 const int maxn = 3e4 + 10; namespace __Coel_FastIO { #ifndef LOCAL #define _getchar_nolock getchar_unlocked #define _putchar_nolock putchar_unlocked #endif inline int read() { int x = 0, f = 1; char ch = _getchar_nolock(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = _getchar_nolock(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = _getchar_nolock(); } return x * f; } inline void write(int x) { if (x < 0) { x = -x; putchar('-'); } static int buf[35]; int top = 0; do { buf[top++] = x % 10; x /= 10; } while (x); while (top) _putchar_nolock(buf[--top] + '0'); } } // namespace __Coel_FastIO using namespace std; using namespace __Coel_FastIO; int n = read(), a[maxn], t[maxn]; int s[maxn * 4]; int ans, gre[maxn], les[maxn]; void get_hash() { memcpy(t, a, sizeof(t)); sort(t + 1, t + n + 1); int* end = unique(t + 1, t + n + 1); for (int i = 1; i <= n; i++) a[i] = lower_bound(t + 1, end, a[i]) - t; } void pushup(int id) { s[id] = s[id * 2] + s[id * 2 + 1]; } int query(int id, int l, int r, int x, int y) { if (x <= l && r <= y) return s[id]; int mid = (l + r) >> 1, res = 0; if (mid >= x) res += query(id * 2, l, mid, x, y); if (mid < y) res += query(id * 2 + 1, mid + 1, r, x, y); return res; } void modify(int id, int l, int r, int v) { if (l == r && l == v) return s[id]++, (void)Flannelette; int mid = (l + r) >> 1; if (mid >= v) modify(id * 2, l, mid, v); else modify(id * 2 + 1, mid + 1, r, v); pushup(id); } signed main(void) { for (int i = 1; i <= n; i++) a[i] = read(); get_hash(); for (int i = 1; i <= n; i++) { if (a[i] > 1) les[i] = query(1, 1, n, 1, a[i] - 1); modify(1, 1, n, a[i]); } memset(s, 0, sizeof(s)); for (int i = n; i >= 1; i--) { if (a[i] < n) gre[i] = query(1, 1, n, a[i] + 1, n); modify(1, 1, n, a[i]); } for (int i = 1; i <= n; i++) ans += 1LL * gre[i] * les[i]; write(ans); return 0; }