#include #include #include #include #define rep(i,a,b) for(int i=(a);i<(b);i++) #define rrep(i,a,b) for(int i=(b)-1;i>=(a);i--) #define all(x) begin(x),end(x) #define sz(x) (int)(x).size() using namespace std; using namespace atcoder; using ll=long long; using ull=unsigned long long; using ld=long double; using vll=vector; using vvll=vector; using vi=vector; using vvi=vector; using pll=pair; using pi=pair; // using mint=modint998244353; using mint=modint1000000007; // using mint2=static_modint<2>; // using BoostPoint=boost::polygon::point_data; // using Voronoi=boost::polygon::voronoi_diagram; // using mint=LazyMontgomeryModInt<998244353>; using vm=vector; using vvm=vector; #line 2 "data-structure/w-ary-tree.hpp" namespace w_ary_tree_impl { using u64 = uint64_t; static constexpr unsigned int lgW = 6; static constexpr unsigned int W = 1u << lgW; static constexpr int inf = 1 << 30; inline int ctz(u64 n) { return n ? __builtin_ctzll(n) : -1; } inline int clz(u64 n) { return n ? 63 - __builtin_clzll(n) : -1; } template struct w_ary_tree_node { u64 map; int mn, mx; static constexpr int shift = (LOG - 1) * lgW; array, W> chd; inline int mask(u64 key) const { return key & ((1 << shift) - 1); } w_ary_tree_node() : map(0), mn(inf), mx(-1) {} void insert(int key) { mn = std::min(mn, key), mx = std::max(mx, key); int pos = key >> shift; map |= 1ULL << pos; chd[pos].insert(mask(key)); } void erase(int key) { int pos = key >> shift; chd[pos].erase(mask(key)); if (chd[pos].map == 0) map &= ~(1ULL << pos); if (mn == key) { if (mx == key) { mn = inf, mx = -1; } else { int p = ctz(map); mn = (p << shift) + chd[p].min(); } } else if (mx == key) { int p = clz(map); mx = (p << shift) + chd[p].max(); } } bool contain(int key) const { int pos = key >> shift; return chd[pos].contain(mask(key)); } inline int min() const { return mn == inf ? -1 : mn; } inline int max() const { return mx; } int find_next(int key) const { if (key <= min()) return min(); int pos = key >> shift; if (((map >> pos) & 1) && mask(key) <= chd[pos].max()) { return (pos << shift) + chd[pos].find_next(mask(key)); } int nxt = ctz(map & ~((1ULL << (pos + 1)) - 1)); if (pos == 63 || nxt == -1) return -1; return (nxt << shift) + chd[nxt].min(); } int find_prev(int key) const { if (max() < key) return max(); int pos = key >> shift; if (((map >> pos) & 1) && chd[pos].min() < mask(key)) { return (pos << shift) + chd[pos].find_prev(mask(key)); } int nxt = clz(map & ((1ULL << pos) - 1ULL)); if (nxt == -1) return -1; return (nxt << shift) + chd[nxt].max(); } }; template struct w_ary_tree_node::type> { u64 map; w_ary_tree_node() : map(0) {} void insert(int key) { map |= 1ULL << key; } void erase(int key) { map &= ~(1ULL << key); } bool contain(int key) const { return (map >> key) & 1; } int min() const { return ctz(map); } int max() const { return clz(map); } int find_next(int key) const { return ctz(map & ~((1ULL << key) - 1)); } int find_prev(int key) const { return clz(map & ((1ULL << key) - 1)); } }; } // namespace w_ary_tree_impl template using w_ary_tree = w_ary_tree_impl::w_ary_tree_node; /** * @brief 64-ary tree */ //online insertion-only prefix max (ならしO(1)) struct frontier{ w_ary_tree<2>S; vi V; frontier(int n):V(n+1){} int query(int r){ int p=S.find_prev(r); return p==-1?0:V[p]; } void insert(int x,int v){ if(v==0)return; int p=S.find_prev(x); if(p!=-1&&V[p]>=v)return; int s=S.find_next(x); while(s!=-1&&V[s]<=v){ S.erase(s); s=S.find_next(x); } S.insert(x); V[x]=v; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); /** * ならしO(N^2) * 解説とほぼ同じです * でもdynamic prefix maxの部分(普通segtreeを使いますが)は**挿入のみ**なので(van Emde Boas treeを使えば)ならし計算量O(QloglogQ)で処理できます(Qは挿入、クエリ回数) * さらにここN<=1000ので、van Emde Boas treeより 64-ary tree(bit高速化)を使えばO(Q)に落とせます */ int T; cin>>T; while(T--){ int N; cin>>N; vi P(N+1),Q(N+1); rep(i,1,N+1)cin>>P[i]; rep(i,1,N+1)cin>>Q[i]; vectorCminus(N+1,frontier(N)),Cplus(N+1,frontier(N)); vi l(N+1),h(N+1); int ans=0; /** * dp[i,j] = 1 + max(max_{xP_i;yQ_j}dp[x,y]) * max_{x