【高手训练】【树状数组】导线问题
题目
哈蒙有\(n\)条导线排成一排,每条导线有一个电阻值,神奇的电光只能从一根导线传到电阻比它大的上面,而且必须从左边向右传导,当然导线不必是连续的。
哈蒙想知道电光最多能通过多少条导线,还想知道这样的方案有多少。
Solution
简化题意:LIS及其方案数,\(a_i \leq n, n \leq 100010\)
最长上升子序列\(n^2\)显然很好做,DP入门题。我们再考虑一下如何实现\(n log_2n\)
我们可以用一个什么数据结构维护,比如树状数组。
对于一个需要转移的\(i\),我们要找的是在\(i\)之前的\(j\),且 满足\(a_j < a_i\)的\(f_j\)的最大值。
树状数组从左至右加入本就保证\(j\)在\(i\)之前,以\(a_i\)为下标建权值树状数组,维护\(f_i\)前缀最大值,这样找出来的\(f_j\)必定合法且最优,直接转移即可。
方案数也没什么问题。
观察一个基本的数据:
\(a_i\) | LIS | 方案数 |
---|---|---|
2 | 1 | 1 |
1 | 1 | 1 |
4 | 2 | 2 |
3 | 2 | 2 |
5 | 3 | 4 |
我们可以理解为
最长上升子序列方案数必定是每层个数的累乘, 而本质就是对于某个长度累加每个方案数,得到下一值的方案数。
而我们只需要在转移的时候把前缀最大值改成累加,注意方案数随最优值更新。
\(\mathrm{Code:}\)
#include <bits/stdc++.h>
const int mod = 123456789;
const int N = 200010;
int n, m;
int a[N] = {};
inline int add(int a, int b) { return a + b >= mod ? a + b - mod : a + b; }
inline void Add(int &a, int b) { a = add(a, b); }
template <class T>
void read(T &s) {
T w = 1;
char c = getchar();
while ((c < '0' || c > '9') && c != '-') c = getchar();
if (c == '-') w = -1, c = getchar();
while (c <= '9' && c >= '0')
s = (s << 1) + (s << 3) + c - '0', c = getchar();
s *= w;
}
template <class T>
void write(T x) {
if (x < 0) x = ~x + 1, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + 48);
return void();
}
int f[N] = {}, s[N] = {};
struct BIT {
int c[N], c1[N];
inline void inc(int x, int v, int s) {
for (++x; x <= N - 10; x += x & (-x)) {
if (v == c[x]) Add(c1[x], s);
if (c[x] < v) c[x] = v, c1[x] = s;
}
}
inline int ask(int x) {
int maxn = -1;
for (++x; x; x -= x & (-x)) maxn = std ::max(maxn, c[x]);
return maxn;
}
inline int Ask(int x) {
int sum = 0, now = 0;
for (++x; x; x -= x & (-x)) {
if (c[x] == now) Add(sum, c1[x]);
if (c[x] > now) now = c[x], sum = c1[x];
}
return sum;
}
} T;
main() {
int maxx = 0;
read(n);
read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
f[1] = 1;
s[1] = 1;
T.inc(a[1], f[1], s[1]);
for (int i = 2; i <= n; ++i) {
f[i] = T.ask(a[i] - 1) + 1;
s[i] = T.Ask(a[i] - 1);
if (f[i] == 1) s[i] = 1;
T.inc(a[i], f[i], s[i]);
maxx = std ::max(maxx, f[i]);
}
write(maxx);
putchar(10);
if (m == 0) return 0;
int ans = 0;
for (int i = 1; i <= n; ++i)
if (f[i] == maxx) Add(ans, s[i]);
write(ans);
return 0;
}