两道题都是区间第K大询问,数据规模基本相同。
解决这种问题, 可以采用平方划分(块状表)复杂度也可以接受,但是实际表现比主席树差得多。
这里大致讲一下我对主席树的理解。
首先,如果对于某个区间【L,R】,对这个区间排序后,我们构造出了线段树, 每个值在这个子区间出现了几次都可以从线段树中查询,我们就可以在对数时间解决询问。
现在的问题就是怎样在时空复杂度O(nlogn)的时间内构造出这么多的线段树!
1.首先对原序列a排序并删去重复得到新序列b
2.对a的每个前缀构造线段树,即a的前i个数出现在b的[L,R]中的个数。
3.相邻前缀的线段树只有一条边不同,故每次只需要增加logn个点,总的空间复杂度O(nlogn)
4.前缀线段树的可加性,rt[r]-rt[l-1]就表示了原序列a[l,r]在新序列b[L,R]的数量。
代码很清晰,参照代码和上述4个性质,应该可以很快理解主席树。
一句话总结一下, 在本题中, 主席树就的功能就是快速的告诉我们:
原序列a的子序列a[l,r]在a排序后的序列b的子序列[L,R]中的个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 100000 + 5; int a[N], b[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20]; int n, k, tot, sz, ql, qr, x, q, T; void Build( int & o, int l, int r){ o = ++ tot; sum[o] = 0; if (l == r) return ; int m = (l + r) >> 1; Build(ls[o], l, m); Build(rs[o], m + 1, r); } void update( int & o, int l, int r, int last, int p){ o = ++ tot; ls[o] = ls[last]; rs[o] = rs[last]; sum[o] = sum[last] + 1; if (l == r) return ; int m = (l + r) >> 1; if (p <= b[m]) update(ls[o], l, m, ls[last], p); else update(rs[o], m + 1, r, rs[last], p); } int query( int ss, int tt, int l, int r, int k){ if (l == r) return l; int m = (l + r) >> 1; int cnt = sum[ls[tt]] - sum[ls[ss]]; if (k <= cnt) return query(ls[ss], ls[tt], l, m, k); else return query(rs[ss], rs[tt], m + 1, r, k - cnt); } void work(){ scanf ( "%d%d%d" , &ql, &qr, &x); int ans = query(rt[ql - 1], rt[qr], 1, sz, x); printf ( "%d\n" , b[ans]); } int main(){ freopen ( "t.txt" , "r" ,stdin); T=1; while (T--){ scanf ( "%d%d" , &n, &q); for ( int i = 1; i <= n; i ++) scanf ( "%d" , a + i), b[i] = a[i]; sort(b + 1, b + n + 1); sz = unique(b + 1, b + n + 1) - (b + 1); tot = 0; Build(rt[0],1, sz); //for(int i = 0; i <= 4 * n; i ++)printf("%d,rt = %d,ls = %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]); //for(int i = 1; i <= n; i ++)a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b; for ( int i = 1; i <= n; i ++)update(rt[i], 1, sz, rt[i - 1], a[i]); // for(int i = 0; i <= 5 * n; i ++)printf("%d,rt = %d,ls = %d, rs = %d, sum = %d\n", i, rt[i], ls[i], rs[i], sum[i]); while (q --)work(); } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步