- D - Excellent Splitting
- Description
- Solution
- Code
D - Excellent Splitting
Description
有一个长度为 \(N\) 的排列 \(P\),将 \(P\) 拆成 \(A\) 和 \(B\),求 \(A\) 的最长上升子序列的长度与 \(B\) 的最长下降子序列的长度相加的最大值。
数据范围:\(1\leqslant N\leqslant 2×10^5\).
Solution
首先一个很重要的结论是,设 LIS/LDS 分别为序列的最长上升/下降子序列长度,答案一定是 LIS+LDS 或者 LIS+LDS-1.
这是因为序列的 LIS 和 LDS 最多重复一个数字,因为要求 严格 上升/下降。
于是只用判断:无论怎么选,上升子序列和下降子序列必定存在一个交点。
使用二元组 \((len,cnt)\) 维护数据,表示最长上升/下降子序列长度为 \(len\),选择子序列方案数为 \(cnt\).
使用 \(f_{0/1,i}\) 表示以 \(i\) 为结尾(\(0\))/开头(\(1\))的最长上升子序列,而 \(g_{0/1,i}\) 表示最长下降子序列。这两个数组均需维护上述二元组。
最后,计算出 \(c_0\) 为全局 LIS 的方案数,\(c_1\) 为全局 LDS 的方案数。那么 \(c_0·c_1\) 表示 LIS 和 LDS 的所有可能匹配。
接着计算 \(h_{0/1,i}\) 表示包含 \(i\) 的最长上升(\(0\))/下降(\(1\))子序列的方案数。于是我们只用比较 \(\sum h_{0,i}\cdot h_{1,i}\) 和 \(c_0·c_1\) 是否相等,如果相等则无论怎么选,上升子序列和下降子序列必定存在一个交点。
这题非常坑爹的一个点是,方案数这一维会爆 long long,不能直接比较,需要自行设定一个模数计算。我用了 \(10^9+7\).
Code
# include <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)template <class T>
inline T read(const T sample) {T x=0; bool f=0; char s;while(!isdigit(s=getchar())) f|=(s=='-');for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);return f? -x: x;
}
template <class T>
inline void write(T x) {static int writ[50], w_tp=0;if(x<0) putchar('-'), x=-x;do writ[++w_tp]=x-x/10*10, x/=10; while(x);while(putchar(writ[w_tp--]^48), w_tp);
}# include <set>
# include <cmath>
# include <vector>
# include <cstring>
# include <iostream>
# include <algorithm>
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;const int MAXN = 2e5+5;
const int mod = 1e9+7;int n, a[MAXN];
ll h[2][MAXN];
struct node {int len; ll cnt;
} c[2][MAXN], f[2][MAXN], g[2][MAXN];int lowbit(int x) { return x&-x; }node merge(node x, node y) {if(x.len ^ y.len)return (x.len<y.len)? y: x;return (node){x.len, (x.cnt+y.cnt)%mod};
}void add0(int x, node k) {for(int i=x; i<=n; i+=lowbit(i))c[0][i] = merge(c[0][i], k);
}void add1(int x, node k) {for(int i=x; i>0; i-=lowbit(i))c[1][i] = merge(c[1][i], k);
}node ask0(int x) {node ret = (node){0, 1};for(int i=x; i>0; i-=lowbit(i))ret = merge(ret, c[0][i]);return ret;
}node ask1(int x) {node ret = (node){0, 1};for(int i=x; i<=n; i+=lowbit(i))ret = merge(ret, c[1][i]);return ret;
}void reset() {for(int i=1; i<=n; ++i)c[0][i] = (node){0, 0},c[1][i] = (node){0, 0};
}void noSananoLife() {n = read(9);for(int i=1; i<=n; ++i) a[i]=read(9);reset();for(int i=1; i<=n; ++i) {node ret0 = ask0(a[i]-1);node ret1 = ask1(a[i]+1);f[0][i] = (node){ret0.len+1, ret0.cnt};g[0][i] = (node){ret1.len+1, ret1.cnt};add0(a[i], f[0][i]);add1(a[i], g[0][i]);}reset();for(int i=n; i>=1; --i) {node ret0 = ask0(a[i]-1);node ret1 = ask1(a[i]+1);g[1][i] = (node){ret0.len+1, ret0.cnt};f[1][i] = (node){ret1.len+1, ret1.cnt};add0(a[i], g[1][i]);add1(a[i], f[1][i]);}int LIS=0, LDS=0;for(int i=1; i<=n; ++i)LIS = max(LIS, f[0][i].len),LDS = max(LDS, g[0][i].len);ll c0=0, c1=0;for(int i=1; i<=n; ++i)c0 += (LIS==f[0][i].len)? f[0][i].cnt: 0,c1 += (LDS==g[0][i].len)? g[0][i].cnt: 0;c0 %= mod, c1 %= mod;for(int i=1; i<=n; ++i) {if(f[0][i].len+f[1][i].len-1 == LIS)h[0][i] = f[0][i].cnt%mod*(f[1][i].cnt%mod)%mod;else h[0][i] = 0;if(g[0][i].len+g[1][i].len-1 == LDS)h[1][i] = g[0][i].cnt%mod*(g[1][i].cnt%mod)%mod;else h[1][i] = 0;}ll all = 0;for(int i=1; i<=n; ++i)all = (all+h[0][i]*h[1][i]%mod)%mod;print(LIS+LDS-(all==c0*c1%mod), '\n');
}int main() {for(int T=read(9); T; --T)noSananoLife();return 0;
}
未完待更。