本文共 1892 字,大约阅读时间需要 6 分钟。
这道题的解法采用了线段树技术,通过两次预处理将问题的时间复杂度优化到了O(n log n)。首先,我们预处理每个元素的前置信息d1[i],表示前i个元素中小于a[i]的元素个数。接着,我们预处理满足三元上升条件的序列对数。
题目要求在数组中找出所有满足三元递增条件的情况。我们需要一个高效算法来处理大规模数据。通过动态规划直接求解会导致时间复杂度过高,因此采用线段树来优化。
为了优化计算过程,我们使用线段树进行预处理:
线段树维护两层信息:区间内元素的数量和d1的累积和。通过两轮预处理,实现高效查询和更新。
using namespace std;#define ll long longconst int maxn = 1e5 + 7;const ll inf = 34359738370;ll tree[maxn * 100];int root = 1, cnt = 1;int d1[maxn], d2[maxn];int n, a[maxn];ll ans = 0;void pushup(int rt) { tree[rt] = tree[lc[rt]] + tree[rc[rt]]; return;}void updata(int &rt, ll l, ll r, ll x, int v) { if (!rt) rt = ++cnt; if (l == r) { tree[rt] += v; if (tree[rt] < 0) tree[rt] = 0; return; } ll mid = (l + r) >> 1; if (x <= mid) updata(lc[rt], l, mid, x, v); else updata(rc[rt], mid + 1, r, x, v); pushup(rt);}int query(int rt, ll l, ll r, ll vl, ll vr) { if (!rt || r < vl || l > vr) return 0; if (vl <= l && r <= vr) return tree[rt]; ll mid = (l + r) >> 1; return query(lc[rt], l, mid, vl, vr) + query(rc[rt], mid + 1, r, vl, vr);}int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", a + i); } updata(root, 0, inf, a[1], 1); for (int i = 2; i <= n; i++) { d1[i] = query(root, 0, inf, 0, a[i] - 1); updata(root, 0, inf, a[i], 1); } for (int i = 1; i <= n; i++) updata(root, 0, inf, a[i], -maxn); for (int i = 2; i <= n; i++) { ans += query(root, 0, inf, 0, a[i] - 1); updata(root, 0, inf, a[i], d1[i]); } printf("%lld\n", ans); return 0;}
通过上述步骤,我们可以高效解决三元递增子序列的问题。
转载地址:http://wkjyk.baihongyu.com/