S O L U T I O N \mathbb{SOLUTION} SOLUTION
状态: F [ i , j ] 表 示 前 i 项 , 递 增 序 列 最 后 一 个 元 素 为 A i 的 方 案 , 递 减 序 列 最 后 一 个 元 素 为 A j 的 方 案 F[i, j] 表示前 i 项, 递增序列最后一个元素为A_i的方案, 递减序列最后一个元素为A_j的方案 F[i,j]表示前i项,递增序列最后一个元素为Ai的方案,递减序列最后一个元素为Aj的方案 G [ i , j ] 表 示 前 i 项 , 递 减 序 列 最 后 一 个 元 素 为 A i 的 方 案 , 递 增 序 列 最 后 一 个 元 素 为 A j 的 方 案 G[i, j] 表示前 i 项, 递减序列最后一个元素为A_i的方案, 递增序列最后一个元素为A_j的方案 G[i,j]表示前i项,递减序列最后一个元素为Ai的方案,递增序列最后一个元素为Aj的方案
A s k : 为 什 么 要 另 外 开 一 个 G [ ] [ ] 状 态 ? Ask: 为什么要另外开一个 G[][] 状态\ ? Ask:为什么要另外开一个G[][]状态 ? A n s w e r : 说 完 转 移 后 , 自 会 提 到 . Answer: 说完转移后,自会提到. Answer:说完转移后,自会提到.
转移:
注: 1 < = j < i < = N 1<=j<i<=N 1<=j<i<=N
i i i 与 i − 1 i-1 i−1 同属一个序列,
A i − 1 < A i A_{i-1}<A_i Ai−1<Ai, F [ i , j ] + = F [ i − 1 , j ] F[i, j] \ \ += \ \ F[i-1, j] F[i,j] += F[i−1,j]; A i − 1 > A i A_{i-1}>A_i Ai−1>Ai, G [ i , j ] + = G [ i − 1 , j ] G[i, j] \ \ += \ \ G[i-1, j] G[i,j] += G[i−1,j].i i i 与 i − 1 i-1 i−1 不同属一个序列,
A j < A i A_j<A_i Aj<Ai, F [ i , i − 1 ] + = G [ i − 1 , j ] ( F [ j , i − 1 ] ) F[i, i-1] \ \ += \ \ G[i-1,j] \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (F[j,i-1]) F[i,i−1] += G[i−1,j] (F[j,i−1]) A j > A i A_j>A_i Aj>Ai, G [ i , i − 1 ] + = F [ i − 1 , j ] ( G [ j , i − 1 ] ) G[i, i-1] \ \ += \ \ F[i-1,j]\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (G[j,i-1]) G[i,i−1] += F[i−1,j] (G[j,i−1])看 到 情 况 2 的 括 号 了 吗 ? 看到情况2的括号了吗? 看到情况2的括号了吗? 这 些 状 态 在 转 移 时 还 没 有 值 , 所 以 额 外 开 了 G [ ] [ ] 来 使 得 这些状态在转移时还没有值,所以额外开了 G[][] 来使得 这些状态在转移时还没有值,所以额外开了G[][]来使得 转 移 可 行 转移可行 转移可行
至此已经完成了 O ( N 2 ) \mathcal{O(N^2)} O(N2) 的算法, 使用树状数组滚动即可优化为 O ( n l o g n ) \mathcal{O(nlogn)} O(nlogn)
可能会出现 不存在某种序列 的情况, 所以要进行特殊处理, 代码中已用 # \# # 注释
C O D E \mathbb{CODE} CODE
#include<bits/stdc++.h> #define reg register int read(){ char c; int s = 0, flag = 1; while((c=getchar()) && !isdigit(c)) if(c == '-'){ flag = -1, c = getchar(); break; } while(isdigit(c)) s = s*10 + c-'0', c = getchar(); return s * flag; } const int maxn = 1500006; const int mod = 666623333; int N; int M; int A[maxn]; struct Tree{ int tap[maxn], v[maxn], tim; void Modify(int k, int x){ while(k <= N){ if(tap[k] != tim) tap[k] = tim, v[k] = 0; v[k] += x; if(v[k] >= mod) v[k] -= mod; k += k & -k; } } int Query(int k){ int s = 0; while(k >= 1){ if(tap[k] != tim) tap[k] = tim, v[k] = 0; s += v[k]; if(s >= mod) s -= mod; k -= k & -k; } return s; } } Ft, Gt; int flag_1, flag_2; // 1↑ 2↓ int main(){ freopen("perm.in", "r", stdin); freopen("perm.out", "w", stdout); N = read(); for(reg int i = 1; i <= N; i ++) A[i] = read(); Ft.Modify(1, 1), Gt.Modify(1, 1); flag_1 = flag_2 = 1; // # for(reg int i = 2; i <= N; i ++){ int tmp_1 = Ft.Query(N-A[i]+1), tmp_2 = Gt.Query(A[i]); if(A[i-1] < A[i]) flag_1 = 0, Gt.tim ++; // # else flag_2 = 0, Ft.tim ++; // # Ft.Modify(N-A[i-1]+1, tmp_2), Gt.Modify(A[i-1], tmp_1); } printf("%d\n", (Ft.Query(N)+Gt.Query(N)-flag_1-flag_2)%mod); return 0; }