树套树专题

对数据结构的不熟练

3196: Tyvj 1730 二逼平衡树

题目链接

尝试一下树状数组套主席树的写法。

小细节没有重视:为了方便起见,一般我们写getRank(x)求的都是<x的数个数。求前驱后继时需要稍微留点心。

  1 #include<bits/stdc++.h>
  2 const int maxn = 50035;
  3 const int maxNode = 5000035;
  4 const int INF = 2147483647;
  5 
  6 struct node
  7 {
  8     int val,l,r;
  9 }f[maxNode];
 10 struct QRs
 11 {
 12     int opt,l,r,k;
 13 }qr[maxn];
 14 int n,m,rt[maxn],v[maxn],tot,cnt[maxn<<1];
 15 int cntl,cntr,L[maxn],R[maxn];
 16 
 17 int read()
 18 {
 19     char ch = getchar();
 20     int num = 0, fl = 1;
 21     for (; !isdigit(ch); ch=getchar())
 22         if (ch=='-') fl = -1;
 23     for (; isdigit(ch); ch=getchar())
 24         num = (num<<1)+(num<<3)+ch-48;
 25     return num*fl;
 26 }
 27 void update(int &rt, int l, int r, int c, int d)
 28 {
 29     if (!rt) rt = ++tot;
 30     f[rt].val += d;
 31     if (l==r) return;
 32     int mid = (l+r)>>1;
 33     if (c <= mid) update(f[rt].l, l, mid, c, d);
 34     else update(f[rt].r, mid+1, r, c, d);
 35 }
 36 int rank(int L, int R, int c)
 37 {
 38     int ret = 0, mid;
 39     for (int i=R; i; i-=i&-i)
 40     {
 41         int u = rt[i], l = 1, r = cnt[0];
 42         while (f[u].val&&l!=r)
 43         {
 44             mid = (l+r)>>1;
 45             if (c <= mid) r = mid, u = f[u].l;
 46             else l = mid+1, ret += f[f[u].l].val, u = f[u].r;
 47         }
 48     }
 49     for (int i=L-1; i; i-=i&-i)
 50     {
 51         int u = rt[i], l = 1, r = cnt[0];
 52         while (f[u].val&&l!=r)
 53         {
 54             mid = (l+r)>>1;
 55             if (c <= mid) r = mid, u = f[u].l;
 56             else l = mid+1, ret -= f[f[u].l].val, u = f[u].r;
 57         }
 58     }
 59     return ret;
 60 }
 61 int ask(int l, int r, int k)
 62 {
 63     if (l==r) return l;
 64     int mid = (l+r)>>1, val = 0;
 65     for (int i=1; i<=cntr; i++) val += f[f[R[i]].l].val;
 66     for (int i=1; i<=cntl; i++) val -= f[f[L[i]].l].val;
 67     if (k <= val){
 68         for (int i=1; i<=cntl; i++) L[i] = f[L[i]].l;
 69         for (int i=1; i<=cntr; i++) R[i] = f[R[i]].l;
 70         return ask(l, mid, k);
 71     }else{
 72         for (int i=1; i<=cntl; i++) L[i] = f[L[i]].r;
 73         for (int i=1; i<=cntr; i++) R[i] = f[R[i]].r;
 74         return ask(mid+1, r, k-val);
 75     }
 76 }
 77 void getRank(int l, int r, int k)
 78 {
 79     for (int j=l-1; j; j-=j&-j) L[++cntl] = rt[j];
 80     for (int j=r; j; j-=j&-j) R[++cntr] = rt[j];
 81     printf("%d\n",cnt[ask(1, cnt[0], k)]);
 82 }
 83 int main()
 84 {
 85     n = read(), m = read();
 86     for (int i=1; i<=n; i++) v[i] = cnt[++cnt[0]] = read();
 87     for (int i=1; i<=m; i++)
 88     {
 89         qr[i].opt = read();
 90         if (qr[i].opt==3) qr[i].l = read();
 91         else qr[i].l = read(), qr[i].r = read();
 92         qr[i].k = cnt[++cnt[0]] = read();
 93     }
 94     cnt[++cnt[0]] = INF, cnt[++cnt[0]] = -INF;
 95     std::sort(cnt+1, cnt+cnt[0]+1);
 96     cnt[0] = std::unique(cnt+1, cnt+cnt[0]+1)-cnt-1;
 97     for (int i=1; i<=n; i++)
 98     {
 99         v[i] = std::lower_bound(cnt+1, cnt+cnt[0]+1, v[i])-cnt;
100         for (int p=i; p<=n; p+=p&-p)
101             update(rt[p], 1, cnt[0], v[i], 1);
102     }
103     for (int opt, i=1; i<=m; i++)
104     {
105         opt = qr[i].opt, cntl = cntr = 0;
106         if (opt!=2) qr[i].k = std::lower_bound(cnt+1, cnt+cnt[0]+1, qr[i].k)-cnt;
107         if (opt==1) printf("%d\n",rank(qr[i].l, qr[i].r, qr[i].k)+1);
108         if (opt==2){
109             getRank(qr[i].l, qr[i].r, qr[i].k);
110         }
111         if (opt==3){
112             for (int p=qr[i].l; p<=n; p+=p&-p)
113                 update(rt[p], 1, cnt[0], v[qr[i].l], -1);
114             v[qr[i].l] = qr[i].k;
115             for (int p=qr[i].l; p<=n; p+=p&-p)
116                 update(rt[p], 1, cnt[0], qr[i].k, 1);
117         }
118         if (opt==4){
119             int rkx = rank(qr[i].l, qr[i].r, qr[i].k);
120             if (!rkx) printf("%d\n",-INF);
121             else getRank(qr[i].l, qr[i].r, rkx);
122         }
123         if (opt==5){
124             int rkx = rank(qr[i].l, qr[i].r, qr[i].k+1);
125             if (rkx>=qr[i].r-qr[i].l+1) printf("%d\n",INF);
126             else getRank(qr[i].l, qr[i].r, rkx+1);
127         }
128     }
129     return 0;
130 }

2141: 排队

题目链接

树套树维护区间逆序对个数;每一次修改只需要处理一下修改前后对答案的正负贡献。

 1 #include<bits/stdc++.h>
 2 const int maxn = 20035;
 3 const int maxNode = 7000035;
 4 
 5 struct node
 6 {
 7     int l,r,val;
 8 }f[maxNode];
 9 int n,m,h[maxn],cnt[maxn];
10 int tot,rt[maxn],cntl,cntr,svl[maxn],svr[maxn];
11 long long ans,tmp;
12 
13 int read()
14 {
15     char ch = getchar();
16     int num = 0, fl = 1;
17     for (; !isdigit(ch); ch=getchar())
18         if (ch=='-') fl = -1;
19     for (; isdigit(ch); ch=getchar())
20         num = (num<<1)+(num<<3)+ch-48;
21     return num*fl;
22 }
23 void update(int &rt, int l, int r, int c, int d)
24 {
25     if (!rt) rt = ++tot;
26     f[rt].val += d;
27     if (l==r) return;
28     int mid = (l+r)>>1;
29     if (c <= mid) update(f[rt].l, l, mid, c, d);
30     else update(f[rt].r, mid+1, r, c, d);
31 }
32 void query(int rt, int L, int R, int l, int r)
33 {
34     if (!rt) return;
35     if (L <= l&&r <= R){
36         tmp += f[rt].val;
37         return;
38     }
39     int mid = (l+r)>>1;
40     if (L <= mid) query(f[rt].l, L, R, l, mid);
41     if (R > mid) query(f[rt].r, L, R, mid+1, r);
42 }
43 long long fndQuery(int lp, int rp, int L, int R)
44 {
45     if (L > R) return 0;
46     long long ret = 0;
47     for (int i=lp,u; i; i-=i&-i)
48         u = rt[i], tmp = 0, query(u, L, R, 1, cnt[0]), ret -= tmp;
49     for (int i=rp,u; i; i-=i&-i)
50         u = rt[i], tmp = 0, query(u, L, R, 1, cnt[0]), ret += tmp;
51     return ret;
52 }
53 void backtrack(int l, int r, int L, int R, int f)
54 {
55     if (l > r||L > R) return;
56     ans += fndQuery(l-1, r, L, R)*f;
57 }
58 int main()
59 {
60     n = read();
61     for (int i=1; i<=n; i++) h[i] = cnt[i] = read();
62     std::sort(cnt+1, cnt+n+1);
63     cnt[0] = std::unique(cnt+1, cnt+n+1)-cnt-1;
64     for (int i=1; i<=n; i++)
65     {
66         h[i] = std::lower_bound(cnt+1, cnt+cnt[0]+1, h[i])-cnt;
67         for (int p=i; p<=n; p+=p&-p) update(rt[p], 1, cnt[0], h[i], 1);
68     }
69     m = read();
70     for (int i=1; i<=n; i++)
71         backtrack(1, i-1, h[i]+1, cnt[0], 1);
72     printf("%lld\n",ans);
73     for (int i=1; i<=m; i++)
74     {
75         int u = read(), v = read();
76         if (u > v) std::swap(u, v);
77         backtrack(1, u-1, h[u]+1, cnt[0], -1);
78         backtrack(u+1, n, 1, h[u]-1, -1);
79         backtrack(1, v-1, h[v]+1, cnt[0], -1);
80         backtrack(v+1, n, 1, h[v]-1, -1);
81         for (int p=u; p<=n; p+=p&-p) update(rt[p], 1, cnt[0], h[u], -1);
82         for (int p=v; p<=n; p+=p&-p) update(rt[p], 1, cnt[0], h[v], -1);
83         if (h[u] > h[v]) ++ans;
84         if (h[u] < h[v]) --ans;
85         std::swap(h[u], h[v]);
86         for (int p=u; p<=n; p+=p&-p) update(rt[p], 1, cnt[0], h[u], 1);
87         for (int p=v; p<=n; p+=p&-p) update(rt[p], 1, cnt[0], h[v], 1);
88         backtrack(1, u-1, h[u]+1, cnt[0], 1);
89         backtrack(u+1, n, 1, h[u]-1, 1);
90         backtrack(1, v-1, h[v]+1, cnt[0], 1);
91         backtrack(v+1, n, 1, h[v]-1, 1);
92         printf("%lld\n",ans);
93     }
94     return 0;
95 }

 

转载于:https://www.cnblogs.com/antiquality/p/10453271.html

<think>嗯,用户想了解线段树套线段树的实现和应用场景。首先,我需要回忆一下什么是线段树套线段树。根据之前的引用内容,比如引用[1]提到,这是一种二维线段树的实现方法,外层处理行,内层处理列。所以,这应该用于处理二维区间的问题,比如矩阵的查询和更新。 接下来,用户可能想知道具体的实现方式。记得引用[2]说过,二维线段树是先按行构建外层线段树,每个节点再按列构建内层线段树。这样分层结构应该能处理二维区域的查询。需要详细说明外层和内层如何协作,可能还要提到每个节点的结构,比如外层节点代表行区间,内层节点处理对应的列区间。 然后应用场景方面,用户可能想知道在哪些实际问题中会用到这种结构。比如矩阵区域求和、最值查询,或者动态二维范围统计,比如游戏中的地图更新。引用[4]提到树套树属于高级数据结构,可能应用在需要高效处理二维数据的场景,比如图像处理或地理信息系统。 另外,需要考虑这种数据结构的优缺点。引用[3]提到其他结构如K-D树或二进制分组可能有不同的适用场景,所以需要对比一下。比如线段树套线段树的时间复杂度是O(logN * logM),适用于静态或动态二维数据,但可能空间消耗较大,因为每个外层节点都有内层线段树。 用户还可能想知道具体的代码结构,比如如何嵌套实现。可能需要分步骤说明,先构建外层,再在每个节点构建内层,然后处理查询和更新操作。例如,更新某个点时,先在外层找到对应的行区间,再在内层更新列的位置。查询时则需要在多个外层和内层节点上聚合结果。 还要注意正确使用LaTeX格式,比如数学表达式要用$...$,独立公式用$$...$$。比如矩阵范围求和可以写成公式,时间复杂度也要用LaTeX表示。 可能用户没有说出来的深层需求是,如何在实际项目中应用这种结构,或者其他数据结构的选择比较。因此,在回答时应该涵盖实现步骤、应用例子以及优缺点比较,帮助用户全面理解。 最后,确保引用来源正确,引用[1]和[2]是关于二维线段树的,而引用[3]和[4]提到了其他树结构和应用场景,可能需要适当引用来说明对比和适用范围。</think>### 线段树套线段树的实现应用场景 #### 一、实现方式 线段树套线段树(二维线段树)是一种嵌套结构,外层线段树管理行维度,每个外层节点再嵌套一棵内层线段树管理列维度[^1][^2]。 **实现步骤**: 1. **外层线段树**:按行构建,每个节点表示行区间 $[L, R]$ 2. **内层线段树**:在每个外层节点中按列构建,处理对应行区间内的列区间查询 3. **数据存储**:外层节点的每个叶子节点对应一行数据,其内层线段树存储该行的列数据 **示例代码框架**(伪代码): ```python class OuterNode: inner_tree = InnerSegmentTree() # 内层线段树 class TwoDimensionalSegmentTree: def build(self, rows): # 外层构建 for row in rows: outer_node = build_outer_tree(row) outer_node.inner_tree.build(row.columns) # 内层构建 def query(self, x1, x2, y1, y2): # 外层查询行区间 [x1, x2],内层查询列区间 [y1, y2] return outer_query(x1, x2).inner_query(y1, y2) def update(self, x, y, value): # 外层定位行x,内层更新列y outer_locate(x).inner_update(y, value) ``` #### 二、时间复杂度分析 - **单点更新**:$O(\log N \cdot \log M)$(外层树深度 $\log N$,内层树深度 $\log M$) - **区域查询**:$O(\log N \cdot \log M)$ - **空间复杂度**:$O(N \cdot M)$(最坏情况下) #### 三、应用场景 1. **动态矩阵区域操作** - 实时更新矩阵元素值 - 查询子矩阵的和/最大值,例如: $$ \sum_{i=a}^{b} \sum_{j=c}^{d} matrix[i][j] $$ 2. **地理信息系统** - 动态统计矩形区域内的点数量(如地图热点分析) 3. **图像处理** - 对图像局部区域进行像素值统计或滤波操作[^2] #### 四、其他结构的对比 | 结构类型 | 适用场景 | 时间复杂度 | 空间复杂度 | |-----------------|--------------------------|------------------|-------------| | 线段树套线段树 | 动态二维区间查询/更新 | $O(\log^2 n)$ | $O(n^2)$ | | K-D Tree | 静态高维近邻搜索 | $O(n^{1-1/d})$ | $O(n)$ | | 二维前缀和 | 静态矩阵区域求和 | $O(1)$查询 | $O(n^2)$ | #### 五、优化方向 1. **惰性标记扩展**:在内层线段树中引入惰性传播,支持区间批量更新 2. **空间压缩**:对稀疏矩阵使用动态开点技术[^3] 3. **混合结构**:对行使用线段树,对列使用树状数组以降低常数因子[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值