军训时候 csy 神仙没能一眼秒掉的神仙题(
突破口:对于相邻两个数相差 \(\pm 1\) 的序列,其最长上升子序列一定是公差为 \(1\) 的等差数列。因为假设存在一个最长上升子序列的两个相邻元素 \(x,y\),满足 \(y-x\ge 2\),那么由于序列中相邻两个数差只能是 \(\pm 1\),它们之间必然存在一个位置上的值等于 \(x+1\),把这个元素选上一定会使 LIS 长度变长。
因此对于第一问,显然最长上升子序列的两个端点会在某一段的开头和结尾取到,因此我们如果求出每一段结尾的数 \(v_i\),那么第一问的答案就是 \(1+\max\limits_{i<j}v_j-v_i\)。
接下来考虑第二问,记第一问的答案为 \(mx\),那么不难发现,如果我们将所有 \(v_j-v_i,j>i\) 的二元组 \((i,j)\) 按 \(j\) 递增的顺序排成一列,那么对应的二元组的 \(v_j\) 必然是单调不增的,并且我们如果把 \(v_j\) 相同的缩成一个连续段,对每个连续段取出最大的 \(j\) 作为连续段的右端点 \(R_k\),取这段对应的 \(i\) 中最小的作为这段的左端点 \(L_k\),那么不难证明,任意两对 \([L_k,R_k]\) 不相交,否则我们将它们并起来肯定会增加 LIS 的长度。
也就是说我们只要对所有 \([L_k,R_k]\) 计算其中有多少个长度为 \(mx\) 的递增序列,然后累加起来就是第二问的答案。注意,由于 \(v_{R_k}-v_{L_k}=mx\),因此这个区间内的 LIS 的值只可能是 \(\{v_{L_k},v_{L_k}+1,\cdots,v_{R_k}\}\),因此考虑以 LIS 值为状态进行转移。设 \(dp_{i,j}\) 表示确定了 \(v_{L_k}\sim i\) 在序列中的位置,目前在第 \(j\) 段的方案数,枚举 \(i+1\) 在序列中处于那一段即可转移。直接转移会爆,不过根据 DP 转移的条件和方程式不难发现,\(v_{L_k}\sim v_{R_k}\) 恰可以被分为 \(\mathcal O(R_k-L_k+1)\) 段,满足这一段中的转移方程式都是相同的,可以使用矩阵快速幂优化,复杂度就是 \(n^4\log v\)。
const int MAXN = 50;
const int MOD = 998244353;
const ll INF = 1e18;
int n, m, a[MAXN + 5], b[MAXN + 5], res;
ll sum[MAXN + 5], mx = 0;
struct mat {
int v[MAXN + 5][MAXN + 5];
mat() {memset(v, 0, sizeof(v));}
mat operator * (const mat &rhs) {
mat res;
for (int i = 1; i <= m; i++) for (int j = 1; j <= m; j++) for (int k = 1; k <= m; k++)
res.v[i][j] = (res.v[i][j] + 1ll * v[i][k] * rhs.v[k][j]) % MOD;
return res;
}
};
int calc(int l, int r) {
// printf("calc %d %d\n", l, r);
m = r - l + 1;
static ll key[MAXN * 2 + 5], uni[MAXN * 2 + 5];
int cnt = 0, num = 0;
for (int i = l + 1; i <= r; i++) {
ll L = sum[i - 1] + ((a[i] > 0) ? 1 : -1), R = sum[i];
if (L > R) swap(L, R);
if (L - 1 >= sum[l]) key[++cnt] = L - 1;
key[++cnt] = R;
}
sort(key + 1, key + cnt + 1); key[0] = -INF;
for (int i = 1; i <= cnt; i++) if (key[i] != key[i - 1]) uni[++num] = key[i];
mat prd;
for (int i = l; i <= r; i++) if (sum[i] == sum[l]) prd.v[1][i - l + 1] = 1;
for (int i = 2; i <= num; i++) {
mat trs; vector<int> vec;
for (int j = l + 1; j <= r; j++) {
ll L = sum[j - 1] + ((a[j] > 0) ? 1 : -1), R = sum[j];
if (L > R) swap(L, R);
if (L <= uni[i] && uni[i] <= R) vec.pb(j);
}
for (int id : vec) for (int j = l; j < id; j++)
trs.v[j - l + 1][id - l + 1] = 1;
for (int id : vec) if (a[id] > 0) trs.v[id - l + 1][id - l + 1] = 1;
// printf("itvl %d [%lld, %lld]\n", i, uni[i - 1] + 1, uni[i]);
// for (int j = 1; j <= m; j++) for (int k = 1; k <= m; k++)
// printf("%d%c", trs.v[j][k], " \n"[k == m]);
ll len = uni[i] - uni[i - 1];
for (; len; len >>= 1, trs = trs * trs) if (len & 1) prd = prd * trs;
// printf("curdp: ");
// for (int j = 1; j <= m; j++) printf("%d%c", prd.v[1][j], " \n"[j == m]);
}
int sum = 0;
for (int i = 1; i <= m; i++) sum = (sum + prd.v[1][i]) % MOD;
return sum;
}
int main() {
scanf("%d%*d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int ed = n, i = (n = 0) + 1; i <= ed; i++) if (a[i]) b[++n] = a[i];
swap(a, b);
if (!n) return puts("1 1"), 0;
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
bool flg = 1;
for (int i = 1; i <= n; i++) flg &= (a[i] < 0);
if (flg) return printf("1 %d\n", (-sum[n] + 1) % MOD), 0;
for (int i = 0; i <= n; i++) for (int j = i + 1; j <= n; j++)
chkmax(mx, sum[j] - sum[i]);
for (int i = 0; i <= n;) {
int pos = -1;
for (int j = i + 1; j <= n; j++) if (sum[j] - sum[i] == mx)
pos = j;
if (~pos) res = (res + calc(i, pos)) % MOD, i = pos + 1;
else i++;
}
printf("%lld %d\n", mx + 1, res);
return 0;
}