线段树学习总结

Posted arrogant-hierarch

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树学习总结相关的知识,希望对你有一定的参考价值。

线段树用途:

  用于区间修改与求和:

    区间修改:

      修改l到r之间的值 , 遍历线段树 , 若某个子节点l<=L && R<=r  ,

      则在该节点上标记修改的值 , 访问该节点及他的子节点时再将标记下传;


线段树可持久化:

  核心: 永远不修改节点上的值 , 只会新建节点 ;

    实现:

    每当修改值的时候 , 新建一个跟节点 , 将他与上一个版本的线段树中未修改的值相连 ,

     对于修改的值新建一个节点 , 当访问到新节点及他的子节点时再动态开点;


zkw非递归线段树:

  建树:

    int t[N*2];

    for(int i=1;i<=n;i++) t[i+N]=a[i];

   单点修改(将下标x上的值改为y):

    t[x+=N]=y;

    for(x>>=1;x;x>>=1)

    {

      t[x]=t[x<<1]+t[x<<1|1];

    }

   区间求和:

    int ans=0;

    for(l+=N-1,r+=N;l^r^1;l>>=1,r>>=1)  // l^r^1用来判断左右节点的父节点是不是同一个节点

    {

      /* 如果当前区间的左端点在他父节点的左子节点上就要加上他的父节点的右子节点,

      相当于左端点包括了右字节点所包括的数*/

      if(!(l^1)) ans+=t[l^1];

      /* 如果当前区间的右端点在他父节点的右子节点上就要加上他的父节点的左子节点*/

      if(r^1) ans+=t[r^1];

    }

   特点:

    不能标记下传; 

    可以用来代替堆优化Dijkstra;


带修改的二维区间求和:

   建树:

    建立一棵范围为[1,n]的线段树,再在这棵线段树的每个节点上建立一棵范围为[1,m]的线段树;


题目:

   【CF260E】 Dividing Kingdom: 9!暴力枚举状态 + 线段树可持久化判断当前状态对错;

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int N = 100010;
  9 
 10 int n;
 11 int a[9];
 12 int cntx, cnty;
 13 int x[N], y[N];
 14 int sum[N * 20], ls[N * 20], rs[N * 20], root[N], tot;
 15 vector<int> ins[N];
 16 
 17 struct Node
 18 {
 19     int x, y;
 20 }t[N];
 21 
 22 void update(int& q, int last, int l, int r, int v)
 23 {
 24     q = ++tot;
 25     ls[q] = ls[last];
 26     rs[q] = rs[last];
 27     sum[q] = sum[last] + 1;
 28 
 29     if(l == r) return;
 30     int mid = (l + r) >> 1;
 31     if (v <= mid) update(ls[q], ls[last], l, mid, v);
 32     else update(rs[q], rs[last], mid + 1, r, v);
 33 }
 34 
 35 int kth(int p, int q, int l, int r, int v) // 找第p版本线段树到第q版本线段树中差值为v的节点
 36 {
 37     if (l == r) return l;
 38 
 39     int s = sum[ls[q]] - sum[ls[p]];
 40     int mid = (l + r) >> 1;
 41     if (s >= v) return kth(ls[p], ls[q], l, mid, v);
 42     else return kth(rs[p], rs[q], mid + 1, r, v - s);
 43 }
 44 
 45 int query(int q, int l, int r, int L, int R) // 求区间和
 46 {
 47     if ((!q) || (L <= l && r <= R)) return sum[q];
 48 
 49     int res = 0;
 50     int mid = (l + r) >> 1;
 51     if (L <= mid) res += query(ls[q], l, mid, L, R);
 52     if (R > mid) res += query(rs[q], mid + 1, r, L, R);
 53     return res;
 54 }
 55 
 56 
 57 int S(int l1, int r1, int l2, int r2) // 求子矩阵 点[l1,l2] 到 点[r1,r2] 之间的点的数量
 58 {
 59     return query(root[r1], 1, cnty, l2, r2) - query(root[l1 - 1], 1, cnty, l2, r2);
 60 }
 61 
 62 bool solve() 
 63 {
 64     int l = 1, r = cntx, ans = 0; // 求在左边的那条竖线
 65     while (l <= r)
 66     {
 67         int mid = (l + r) >> 1;
 68         if (sum[root[mid]] >= a[0] + a[1] + a[2])
 69         {
 70             ans = mid;
 71             r = mid - 1;
 72         }
 73         else l = mid + 1;
 74     }
 75     if (ans == 0 || sum[root[ans]] != a[0] + a[1] + a[2]) return false; // 没找到这条线
 76     int p1 = ans;
 77 
 78     l = 1, r = cntx, ans = 0; // 求在右边的那条竖线
 79     while (l <= r)
 80     {
 81         int mid = (l + r) >> 1;
 82         if (sum[root[mid]] >= a[0] + a[1] + a[2] + a[3] + a[4] + a[5])
 83         {
 84             ans = mid;
 85             r = mid - 1;
 86         }
 87         else l = mid + 1;
 88     }
 89     if (ans == 0 || sum[root[ans]] != a[0] + a[1] + a[2] + a[3] + a[4] + a[5]) return false; // 没找到这条线
 90     int p2 = ans;
 91 
 92     int posx1 = kth(root[0], root[p1], 1, cnty, a[0]); // 找上面那条横线
 93     while (S(1, p1, 1, posx1) != a[0] || S(p1 + 1, p2, 1, posx1) != a[3] || S(p2 + 1, cntx, 1, posx1) != a[6])
 94     {
 95         if (S(1, p1, 1, posx1) != a[0]) return false; // 找不到
 96         ++posx1;
 97     }
 98 
 99     int posx2 = kth(root[0], root[p1], 1, cnty, a[0] + a[1]); // 找下面那条横线
100     while (S(1, p1, posx1+1, posx2) != a[1] || S(p1 + 1, p2, posx1 + 1, posx2) != a[4] || S(p2 + 1, cntx, posx1 + 1, posx2) != a[7])
101     {
102         if (S(1, p1, posx1 + 1, posx2) != a[1]) return false; // 找不到
103         ++posx2;
104     }
105 
106     printf("%.10lf %.10lf 
%.10lf %.10lf", x[p1] + 0.5, x[p2] + 0.5, y[posx1] + 0.5, y[posx2] + 0.5);
107     return true;
108 }
109 
110 bool com(Node a, Node b)
111 {
112     return a.x == b.x ? a.y < b.y : a.x < b.x;
113 }
114 
115 int main()
116 {
117     scanf("%d", &n);
118     for (int i = 1; i <= n; i++)
119     {
120         scanf("%d%d", &t[i].x, &t[i].y);
121         x[++cntx] = t[i].x;
122         y[++cnty] = t[i].y;
123     }
124     for (int i = 0; i < 9; i++) scanf("%d", &a[i]);
125 
126     sort(x + 1, x + 1 + cntx);
127     cntx = unique(x + 1, x + 1 + cntx) - (x + 1);
128     sort(y + 1, y + 1 + cnty);
129     cnty = unique(y + 1, y + 1 + cnty) - (y + 1);
130     sort(t + 1, t + 1 + n, com);
131 
132     for (int i = 1; i <= n; i++) // 哈希
133     {
134         t[i].x = lower_bound(x + 1, x + cntx + 1, t[i].x) - x;
135         t[i].y = lower_bound(y + 1, y + cnty + 1, t[i].y) - y;
136         ins[t[i].x].push_back(t[i].y);
137     }
138 
139     for (int i = 1; i <= cntx; i++) // 建可持久化线段树
140     {
141         root[i] = root[i - 1];
142         for (int j = 0; j < ins[i].size(); j++)
143         {
144             update(root[i], root[i], 1, cnty, ins[i][j]);
145         }
146     }
147 
148     int loop = 362880;
149     while (loop--)
150     {
151         next_permutation(a, a + 9); // 全排列
152         if (solve()) return 0;
153     }
154 
155     puts("-1");
156 }

 

 

 

 

 

 

技术图片

 

以上是关于线段树学习总结的主要内容,如果未能解决你的问题,请参考以下文章

线段树详解

树链剖分总结线段树维护

集训总结

线段树模板总结

P1243~P1247 线段树模板题总结

2019暑假集训 8/2