Problem - 1474F - Codeforces
经典题。
首先最长严格上升子序列可以直接求得。
之后将其分成若干上升和下降的段,显然这样的段的值域肯定是连续的,或者说我们的最长严格上升子序列值域是连续的。
考虑对于这个东西进行 $\tt Dp$,发现我们只需要知道答案的最小值和最大值就可以确定一个数列。如果有多种这样的答案,对于任意两个答案序列肯定是不交的。
如果相交就可以获得更长的答案。
所以对于这个东西可以分开计算,具体来说就是枚举最小值,之后考虑将序列分成上升和下降的段。
发现按照段进行转移是不方便的, 所以考虑按照值域进行转移。
我们考虑枚举之后设 $f(i, j)$ 表示当前值为 $i$,处于第 $j$ 段的方案数。
这个东西可以矩阵加速,复杂度是 $O(n \times n^3 \log V)$。
发现一个问题,如果直接只考虑左右端点的话会发现部分转移没法转移到。
所以需要拆点拆成一个可以使其转移到的位置,也就是拆成 $3$ 份。
我们具体来说说:
1 2 3 4 5 6 7 8 9 10
| for(i = 1; i <= totd; ++ i) { if(d[i] <= last || d[i] > 1 + ans) continue; B.clear(); for(j = 1; j <= tot; ++ j) if(min(l[j], r[j]) <= last + 1 && max(l[j], r[j]) >= d[i]) { for(int k = 0; k <= j; ++ k) B[k][j] = 1; if(l[j] >= r[j]) B[j][j] = 0; } ksm(d[i] - last), last = d[i]; }
|
这个是之前的转移,其中 $d_i$ 是所有左右端点排序去重之后的结果。
对于第 $2$ 个样例。我们发现在 $d_1 = 1, d_2 = 3$ 的时候,理论上应该有如下结果:
1 2
| di = 1 : 0 1 0 0 di = 3 : 0 1 1 0
|
但是却出现了:
1 2
| di = 1 : 0 1 0 0 di = 3 : 0 1 0 0
|
发现进行该转移的应是一个下降序列,但是下降序列转移本质上只能选择一次,因为 min(l[j], r[j]) <= last + 1
这个条件没有满足所以没有进行转移,所以我们必须再钦定一个转移的方案,不妨考虑将 $l_i$ 多拆成 $l_i - 1$,这样就可以进行转移了。
对于上升也是同理的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
| #include <bits/stdc++.h> using namespace std; namespace Legendgod { namespace Read {
#ifdef Fread const int Siz = (1 << 21) + 5; char *iS, *iT, buf[Siz]; #define gc() ( iS == iT ? (iT = (iS = buf) + fread(buf, 1, Siz, stdin), iS == iT ? EOF : *iS ++) : *iS ++ ) #define getchar gc #endif template <typename T> void r1(T &x) { x = 0; char c(getchar()); int f(1); for(; !isdigit(c); c = getchar()) if(c == '-') f = -1; for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48); x *= f; } template <typename T, typename...Args> void r1(T &x, Args&...arg) { r1(x), r1(arg...); } #undef getchar }
using namespace Read;
const int maxn = 50 + 5; const int mod = 998244353; typedef long long int64; int n, m, a[maxn];
struct Matrix { int a[maxn][maxn]; void clear() { memset(a, 0, sizeof(a)); } Matrix(void) { clear(); } int* operator [] (const int& x) { return a[x]; } const int* operator [] (const int& x) const { return a[x]; } Matrix operator * (const Matrix& z) const { Matrix res; for(int i = 0; i <= m; ++ i) { for(int j = 0; j <= m; ++ j) { for(int k = 0; k <= m; ++ k) { res[i][j] = (res[i][j] + 1ll * a[i][k] * z[k][j]) % mod; } } } return res; } }A, B;
int tot(0), totd(0); int64 d[maxn * 6], l[maxn], r[maxn];
void ksm(int mi) {
while(mi) { if(mi & 1) A = A * B; mi >>= 1; B = B * B; } }
int64 ans(0);
int Solve(int L,int R) {
A.clear(); tot = totd = 0; int64 tmp(1); A[0][0] = 1; for(int i = L; i <= R; tmp += a[i], ++ i) { l[++ tot] = (a[i] > 0) ? tmp + 1 : tmp - 1; r[tot] = tmp + a[i]; if(tot == 1) l[tot] = 1; d[++ totd] = l[tot], d[++ totd] = r[tot]; d[++ totd] = l[tot] - 1, d[++ totd] = r[tot] - 1; d[++ totd] = l[tot] + 1, d[++ totd] = r[tot] + 1;
} sort(d + 1, d + totd + 1); totd = unique(d + 1, d + totd + 1) - d - 1;
m = tot; int64 last = 0; int i, j; for(i = 1; i <= totd; ++ i) { if(d[i] <= last || d[i] > 1 + ans) continue; B.clear(); for(j = 1; j <= tot; ++ j) if(min(l[j], r[j]) <= last + 1 && max(l[j], r[j]) >= d[i]) { for(int k = 0; k <= j; ++ k) B[k][j] = 1; if(l[j] >= r[j]) B[j][j] = 0; } ksm(d[i] - last); last = d[i]; } int64 res(0); for(i = 0; i <= tot; ++ i) res += A[0][i];
return res % mod; }
signed main() { int i, j; r1(n, i); for(i = 1; i <= n; ++ i) { r1(a[i]); if(a[i] == 0) -- i, -- n; } if(n == 0) return puts("1 1"), 0; int64 sum(0), ssum(0); for(i = 0; i <= n; ++ i) { sum = a[i]; ssum += a[i]; for(j = i + 1; j <= n; ++ j) { sum += a[j]; if(sum > a[i]) ans = max(ans, sum - a[i]); } } if(ans == 0) return printf("1 %lld\n", (1 - ssum) % mod), 0; int64 res(0); for(i = 1; i <= n; ++ i) { int rt = -1; int64 tmp(0), st(0); for(j = i; j <= n; ++ j) { tmp += a[j]; if(tmp - st == ans) rt = j; } if(rt == -1) continue; res += Solve(i, rt), i = rt; } printf("%lld %lld\n", (ans + 1), res % mod); return 0; }
}
signed main() { return Legendgod::main(), 0; }
|