题意
给一个长度为\(n\)的排列\(P\),求对于\(1 - n\)中的每个数\(m\),是否能找到一段长度为\(m\)的区间使得区间内的数是一个\(1 - m\)的排列
输出一个\(01\)串,其中第\(i\)位表示是否能找到一段长度为\(i\)的区间使得区间内的数是一个\(1 - i\)的排列
\(n \leq 2e5\)
分析
如果对于某个数能找到一段区间使它合法,那么这个区间一定是唯一且连续的
考虑从小到大对于每个数查找它的位置并维护当前所找到的位置的最小值和最大值,即维护一下区间的左端点和右端点
当这个区间连续时,这个区间的长度\(len\)是合法的
于是现在变成了在一个无序的排列上查找一个数的位置,主席树+二分即可
代码
#include<bits/stdc++.h> using namespace std; inline int read() { int cnt = 0, f = 1; char c = getchar(); while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();} while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();} return cnt * f; } const int N = 200000 + 10; int T, n, a[N], L, R, p; int cur, rt[N], ls[N * 20], rs[N * 20], val[N * 20]; void modify(int &x, int l, int r, int lst, int pos) { x = ++cur; ls[x] = ls[lst], rs[x] = rs[lst], val[x] = val[lst] + 1; if (l == r) return; int mid = (l + r) >> 1; if (pos <= mid) modify(ls[x], l, mid, ls[lst], pos); else modify(rs[x], mid + 1, r, rs[lst], pos); } int query(int x, int l, int r, int pos) { if (!x) return 0; if (l == r) return val[x]; int mid = (l + r) >> 1; if (pos <= mid) return query(ls[x], l, mid, pos); else return query(rs[x], mid + 1, r, pos); } void clear() { for (register int i = 1; i <= cur; ++i) ls[i] = rs[i] = val[i] = 0; memset(rt, 0, sizeof rt); cur = 0; L = n + 1, R = 0; } int binary(int d) { int l = 1, r = n; int mid = (l + r) >> 1; while (l < r) { if (!query(rt[mid], 1, n, d)) l = mid + 1; else r = mid; mid = (l + r) >> 1; } return l; } int main() { // freopen("1.in", "r", stdin); T = read(); while (T--) { n = read(); clear(); for (register int i = 1; i <= n; ++i) a[i] = read(), modify(rt[i], 1, n, rt[i - 1], a[i]); for (register int i = 1; i <= n; ++i) { p = binary(i); if (p < L) L = p; if (p > R) R = p; if (R - L + 1 == i) printf("1"); else printf("0"); } puts(""); } return 0; }