线段树学习总结
线段树用途:
用于区间修改与求和:
区间修改:
修改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 \n%.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 }
【CF266E】 More Queries to Array... (题目):
将 后面的 ( i-l+1)k 通过二项式定理
拆开变成
然后再将式子 变成
预处理出C(i,j), 记录 ij 的前缀和 , 再通过线段树存储, 最后求解
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 100010, mod = 1000000007; 8 9 int n, m; 10 int t[N * 4][6], lazy[N * 4]; // t[i][j]表示子节点i在j次方下的线段树 lazy懒标记 11 int power[6][N], a[N]; 12 int C[6][6]; 13 14 struct Node 15 { 16 int d[6]; 17 }; 18 19 signed mo(int x, int y) 20 { 21 return x + y >= mod ? x + y - mod : x + y; 22 } 23 24 void init() 25 { 26 C[0][0] = 1; 27 for (int i = 1; i < 6; i++) 28 { 29 C[i][0] = C[i][i] = 1; 30 for (int j = 1; j < i; j++) 31 { 32 C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; 33 } 34 } 35 36 for (int i = 0; i < N; i++) power[0][i] = 1; 37 38 for (int j = 1; j < 6; j++) // 求i的j次方 39 { 40 for (int i = 1; i < N; i++) 41 { 42 power[j][i] = 1ll * power[j - 1][i] * i % mod; 43 } 44 } 45 46 for (int j = 0; j < 6; j++) // 求次方的前缀和 47 { 48 for (int i = 1; i < N; i++) 49 { 50 power[j][i] = mo(power[j][i], power[j][i - 1]); 51 } 52 } 53 } 54 55 int get(int q, int l, int r) 56 { 57 return mo(power[q][r], mod - power[q][l - 1]); 58 } 59 60 void pushup(int q) 61 { 62 for (int i = 0; i < 6; i++) 63 { 64 t[q][i] = mo(t[q << 1][i], t[q << 1 | 1][i]); 65 } 66 } 67 68 void pushdown(int q, int l, int mid, int r) 69 { 70 if (~lazy[q]) 71 { 72 lazy[q << 1] = lazy[q << 1 | 1] = lazy[q]; 73 74 for (int j = 0; j < 6; j++) 75 { 76 t[q << 1][j] = 1ll * lazy[q] * get(j, l, mid) % mod; 77 t[q << 1 | 1][j] = 1ll * lazy[q] * get(j, mid + 1, r) % mod; 78 } 79 lazy[q] = -1; 80 } 81 } 82 83 void build(int q, int l, int r) 84 { 85 lazy[q] = -1; 86 if (l == r) 87 { 88 for (int j = 0; j < 6; j++) 89 { 90 t[q][j] = 1ll * a[l] * get(j, l, l) % mod; 91 } 92 return; 93 } 94 95 int mid = (l + r) >> 1; 96 build(q << 1, l, mid); 97 build(q << 1 | 1, mid + 1, r); 98 pushup(q); 99 } 100 101 void update(int q, int l, int r, int L, int R, int x) 102 { 103 if (L == l && R == r) 104 { 105 lazy[q] = x; 106 for (int i = 0; i < 6; i++) 107 { 108 t[q][i] = 1ll * x * get(i, l, r) % mod; 109 } 110 return; 111 } 112 113 int mid = (l + r) >> 1; 114 pushdown(q, l, mid, r); 115 116 if (R <= mid) update(q << 1, l, mid, L, R, x); 117 else if (L > mid) update(q << 1 | 1, mid + 1, r, L, R, x); 118 else 119 { 120 update(q << 1, l, mid, L, mid, x); 121 update(q << 1 | 1, mid + 1, r, mid + 1, R, x); 122 } 123 pushup(q); 124 } 125 126 Node query(int q, int l, int r, int L, int R) 127 { 128 Node res; 129 if (l == L && r == R) 130 { 131 for (int i = 0; i < 6; i++) 132 { 133 res.d[i] = t[q][i]; 134 } 135 return res; 136 } 137 138 Node resl, resr; 139 int mid = (l + r) >> 1; 140 pushdown(q, l, mid, r); 141 142 for (int j = 0; j < 6; j++) resl.d[j] = resr.d[j] = 0; 143 144 if (R <= mid) resl = query(q << 1, l, mid, L, R); 145 else if (L > mid) resr = query(q << 1 | 1, mid + 1, r, L, R); 146 else 147 { 148 resl = query(q << 1, l, mid, L, mid); 149 resr = query(q << 1 | 1, mid + 1, r, mid + 1, R); 150 } 151 152 for (int i = 0; i < 6; i++) 153 { 154 resl.d[i] = mo(resl.d[i], resr.d[i]); 155 } 156 return resl; 157 } 158 159 int main() 160 { 161 init(); 162 163 scanf("%d%d", &n, &m); 164 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 165 166 build(1, 1, n); 167 while (m--) 168 { 169 char c; 170 while (scanf("%c", &c), c != '?' && c != '='); 171 172 if (c == '=') 173 { 174 int l, r, x; 175 scanf("%d%d%d", &l, &r, &x); 176 update(1, 1, n, l, r, x); 177 } 178 else 179 { 180 int l, r, k; 181 scanf("%d%d%d", &l, &r, &k); 182 Node u = query(1, 1, n, l, r); 183 184 int res, ans = 0; 185 for (int j = 0; j <= k; j++) 186 { 187 if (l > 1) res = get(k - j, l - 1, l - 1); 188 else res = power[k - j][l - 1]; 189 if (((k ^ j) & 1) && res) res = mod - res; 190 ans = mo(ans, 1ll * res * C[k][j] % mod * u.d[j] % mod); 191 } 192 printf("%d\n", ans); 193 } 194 195 } 196 }
【CF323C】 Two permutations (题目):
把两个序列想象成矩阵,第一个序列为行,第二个序列为列,a[i]=b[j]=x表示x这个点在矩阵[j,i]上,再用主席树(可持久化线段树)求子区间和;
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 1000010, M = 40000010; 8 9 int n; 10 int id[N], a[N]; 11 int root[N * 4], tot; 12 int ls[M], rs[M], sum[M]; 13 14 void update(int& q, int k, int l, int r, int v) 15 { 16 q = ++tot; 17 ls[q] = ls[k]; 18 rs[q] = rs[k]; 19 sum[q] = sum[k] + 1; 20 21 if (l == r) return; 22 23 int mid = (l + r) >> 1; 24 if (v <= mid) update(ls[q], ls[k], l, mid, v); 25 else update(rs[q], rs[k], mid + 1, r, v); 26 } 27 28 int f(int a,int x) 29 { 30 return ((a - 1 + x) % n) + 1; 31 } 32 33 int query(int q, int l, int r, int L, int R) 34 { 35 if (l == L && r == R) 36 { 37 return sum[q]; 38 } 39 40 int mid = (l + r) >> 1; 41 if (R <= mid) return query(ls[q], l, mid, L, R); 42 else if (L > mid) return query(rs[q], mid + 1, r, L, R); 43 else return query(ls[q], l, mid, L, mid) + query(rs[q], mid + 1, r, mid + 1, R); 44 } 45 46 int main() 47 { 48 scanf("%d", &n); 49 for (int i = 1; i <= n; i++) 50 { 51 int c; 52 scanf("%d", &c); 53 id[c] = i; 54 } 55 for (int i = 1; i <= n; i++) 56 { 57 int c; 58 scanf("%d", &c); 59 a[i] = id[c]; 60 } 61 62 for (int i = 1; i <= n; i++) 63 { 64 update(root[i], root[i - 1], 1, n, a[i]); 65 } 66 67 int m, ans = 0; 68 scanf("%d", &m); 69 while (m--) 70 { 71 int a, b, c, d; 72 scanf("%d%d%d%d", &a, &b, &c, &d); 73 int l1, r1, l2, r2; 74 l1 = min(f(a, ans), f(b, ans)), r1 = max(f(a, ans), f(b, ans)); 75 l2 = min(f(c, ans), f(d, ans)), r2 = max(f(c, ans), f(d, ans)); 76 77 printf("%d\n", ans = query(root[r2], 1, n, l1, r1) - query(root[l2 - 1], 1, n, l1, r1)); 78 ans++; 79 } 80 }
【CF240F】TorCoder(题目):
建立26个线段树分别代表a~z,当要执行区间【l,r】时,枚举每一个字母在这个区间中的数量,若区间长度为奇,则要有一个奇数个数的字母;区间长度为偶,则不能有奇数个数的字母。
判断是否要执行这个区间后,从a枚举到z将字母放入其中
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef long long ll; 8 9 const int INF = 0x3f3f3f3f; 10 11 #define lc(a) ((a) << 1) 12 #define rc(a) ((a) << 1 | 1) 13 14 int cnt[30]; 15 char s[100010]; 16 17 struct tree 18 { 19 int l; 20 int r; 21 int sum[30]; 22 int lazy; 23 24 void clear() 25 { 26 memset(sum, 0, sizeof(sum)); 27 } 28 }; 29 30 tree operator + (tree a, tree b) 31 { 32 if (a.l == -1) return b; 33 if (b.l == -1) return a; 34 35 tree ret; 36 ret.l = a.l; 37 ret.r = b.r; 38 for (int i = 0; i < 26; i++) ret.sum[i] = a.sum[i] + b.sum[i]; 39 return ret; 40 } 41 42 struct Tree 43 { 44 tree t[800010]; 45 46 void push_up(int q) 47 { 48 for (int i = 0; i < 26; i++) t[q].sum[i] = t[lc(q)].sum[i] + t[rc(q)].sum[i]; 49 } 50 51 void push_down(int q) 52 { 53 if (t[q].lazy != -1) 54 { 55 t[lc(q)].clear(); 56 t[rc(q)].clear(); 57 t[lc(q)].sum[t[q].lazy] = t[lc(q)].r - t[lc(q)].l + 1; 58 t[rc(q)].sum[t[q].lazy] = t[rc(q)].r - t[rc(q)].l + 1; 59 t[lc(q)].lazy = t[rc(q)].lazy = t[q].lazy; 60 } 61 t[q].lazy = -1; 62 } 63 64 void build(int q, int l, int r) 65 { 66 t[q].lazy = -1; 67 t[q].l = l; 68 t[q].r = r; 69 70 if (l == r) 71 { 72 t[q].clear(); 73 t[q].sum[s[l] - 'a'] = 1; 74 return; 75 } 76 77 int mid = (l + r) >> 1; 78 build(lc(q), l, mid); 79 build(rc(q), mid + 1, r); 80 push_up(q); 81 } 82 83 void query(int q, int l, int r) 84 { 85 tree nul; 86 nul.l = -1; 87 if (t[q].l > r || t[q].r < l) return; 88 89 push_down(q); 90 if (t[q].l >= l && t[q].r <= r) 91 { 92 for (int i = 0; i < 26; i++) cnt[i] += t[q].sum[i]; 93 return; 94 } 95 query(lc(q), l, r); 96 query(rc(q), l, r); 97 } 98 99 void update(int q, int l, int r, int c) 100 { 101 if (t[q].l > r || t[q].r < l) return; 102 103 if (t[q].l >= l && t[q].r <= r) 104 { 105 t[q].clear(); 106 t[q].sum[c] = t[q].r - t[q].l + 1; 107 t[q].lazy = c; 108 return; 109 } 110 111 push_down(q); 112 update(lc(q), l, r, c); 113 update(rc(q), l, r, c); 114 push_up(q); 115 } 116 117 void print(int q) 118 { 119 if (t[q].l == t[q].r) 120 { 121 for (int i = 0; i < 26; i++) 122 { 123 if (t[q].sum[i]) putchar('a' + i); 124 } 125 return; 126 } 127 128 push_down(q); 129 print(lc(q)); 130 print(rc(q)); 131 } 132 }t; 133 134 int main() 135 { 136 freopen("input.txt", "r", stdin); 137 freopen("output.txt", "w", stdout); 138 139 int n, m; 140 scanf("%d%d", &n, &m); 141 scanf("%s", s + 1); 142 143 t.build(1, 1, n); 144 while (m--) 145 { 146 int l, r; 147 scanf("%d%d", &l, &r); 148 149 memset(cnt, 0, sizeof(cnt)); 150 t.query(1, l, r); 151 if ((r - l + 1) & 1) 152 { 153 int cs = 0, num; 154 for (int i = 0; i < 26; i++) 155 { 156 if (cnt[i] & 1) 157 { 158 cs++; 159 num = i; 160 } 161 } 162 163 if (cs == 1) 164 { 165 int mid = (l + r) >> 1; 166 t.update(1, mid, mid, num); 167 int cc = 0; 168 for (int i = 0; i < 26; i++) 169 { 170 if (cnt[i] <= 1) continue; 171 172 int c2 = cc + (cnt[i] >> 1); 173 t.update(1, l + cc, l + c2 - 1, i); 174 t.update(1, r - c2 + 1, r - cc, i); 175 cc = c2; 176 } 177 } 178 } 179 else 180 { 181 int pd = 0, num = -1; 182 183 for (int i = 0; i < 26; i++) 184 { 185 if (cnt[i] & 1) 186 { 187 pd++; 188 num = i; 189 } 190 } 191 192 if (pd == 0) 193 { 194 int c = 0; 195 for (int i = 0; i < 26; i++) 196 { 197 if (cnt[i] == 0) continue; 198 199 int c2 = c + (cnt[i] >> 1); 200 t.update(1, l + c, l + c2 - 1, i); 201 t.update(1, r - c2 + 1, r - c, i); 202 c = c2; 203 } 204 } 205 } 206 } 207 t.print(1); 208 }
【SHOI2008】堵塞的交通 (题目):
线段树记录该子节点区间四个顶点的联通情况
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int maxn = 400010; 8 9 #define lson o<<1,l,mid 10 #define rson o<<1|1,mid+1,r 11 12 struct Node 13 { 14 int lft, rig; 15 bool l, r, u, d, p, q; 16 }seg[maxn]; 17 18 int n; 19 bool conn[maxn][2]; 20 21 void pushup(Node& x, Node l, Node r) 22 { 23 x.lft = l.lft; 24 x.rig = r.rig; 25 26 x.l = l.l | (l.u & conn[l.rig][0] & r.l & conn[l.rig][1] & l.d); 27 x.r = r.r | (r.u & conn[l.rig][0] & l.r & conn[l.rig][1] & r.d); 28 29 x.u = (l.u & conn[l.rig][0] & r.u) | (l.p & conn[l.rig][1] & r.q); 30 x.d = (l.d & conn[l.rig][1] & r.d) | (l.q & conn[l.rig][0] & r.p); 31 32 x.p = (l.u & conn[l.rig][0] & r.p) | (l.p & conn[l.rig][1] & r.d); 33 x.q = (l.d & conn[l.rig][1] & r.q) | (l.q & conn[l.rig][0] & r.u); 34 } 35 36 void build(int o, int l, int r) 37 { 38 if (l == r) 39 { 40 seg[o].lft = seg[o].rig = l; 41 seg[o].u = seg[o].d = 1; //左右联通 42 return; 43 } 44 45 int mid = (l + r) >> 1; 46 build(lson); 47 build(rson); 48 pushup(seg[o], seg[o << 1], seg[o << 1 | 1]); 49 } 50 51 void update1(int o, int l, int r, int p, int row, bool val) //同行修改 52 { 53 int mid = (l + r) >> 1; 54 if (mid == p) 55 { 56 conn[mid][row] = val; 57 pushup(seg[o], seg[o << 1], seg[o << 1 | 1]); 58 return; 59 } 60 61 if (mid >= p) update1(lson, p, row, val); 62 else update1(rson, p, row, val); 63 pushup(seg[o], seg[o << 1], seg[o << 1 | 1]); 64 } 65 66 void update2(int o, int l, int r, int p, bool val) //不同行修改,第p列 67 { 68 if (l == r) 69 { 70 seg[o].l = seg[o].r = seg[o].p = seg[o].q = val; 71 return; 72 } 73 74 int mid = (l + r) >> 1; 75 if (mid >= p) update2(lson, p, val); 76 else update2(rson, p, val); 77 pushup(seg[o], seg[o << 1], seg[o << 1 | 1]); 78 } 79 80 Node query(int o, int l, int r, int ql, int qr) 81 { 82 if (l >= ql && r <= qr) return seg[o]; 83 84 int mid = (l + r) >> 1; 85 if (mid < ql) return query(rson, ql, qr); 86 if (mid >= qr) return query(lson, ql, qr); 87 88 Node ans; 89 pushup(ans, query(lson, ql, qr), query(rson, ql, qr)); 90 return ans; 91 } 92 93 int main() 94 { 95 scanf("%d", &n); 96 97 char op[10]; 98 build(1, 1, n); 99 while (scanf("%s", op) , op[0] != 'E') 100 { 101 int a, b, c, d; 102 scanf("%d%d%d%d", &a, &b, &c, &d); 103 104 if (op[0] == 'C') //删边 105 { 106 if (a == c) update1(1, 1, n, min(b, d), a - 1, 0); //同一行 107 else update2(1, 1, n, b, 0); //不同行 108 } 109 110 if (op[0] == 'O') //加边 111 { 112 if (a == c) update1(1, 1, n, min(b, d), a - 1, 1); 113 else update2(1, 1, n, b, 1); 114 } 115 116 if (op[0] == 'A') //询问 117 { 118 if (b > d) swap(a, c), swap(b, d); 119 Node ans1 = query(1, 1, n, b, d), ans2 = query(1, 1, n, 1, b), ans3 = query(1, 1, n, d, n); 120 bool flag = false; 121 if (a == 1) 122 { 123 if (c == 1) //左上,右上 124 { 125 if (ans1.u) flag = true; 126 if (ans2.r & ans1.d & ans3.l) flag = true; 127 } 128 else //左上,右下 129 { 130 if (ans1.p) flag = true; 131 if (ans2.r & ans1.d) flag = true; 132 if (ans3.l & ans1.u) flag = true; 133 } 134 } 135 else 136 { 137 if (c == 1) //左下,右上 138 { 139 if (ans1.q) flag = true; 140 if (ans2.r & ans1.u) flag = true; 141 if (ans3.l & ans1.d) flag = true; 142 } 143 else //左下,右下 144 { 145 if (ans1.d) flag = true; 146 if (ans2.r & ans1.u & ans3.l) flag = true; 147 } 148 } 149 puts(flag ? "Y" : "N"); 150 } 151 } 152 }
【THUPC 2017】天天爱射击 (题目):
把子弹坐标当做版本,时间当做值,对于每一个木板,如果它能被打碎,则在木板的区间内找到的第s个点就是打碎它的子弹;
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 200010, M=8000010; 9 10 int n, m; 11 int root[N], tot; 12 int ls[M], rs[M], sum[M]; 13 int res[N]; 14 vector<int> t[N]; 15 16 struct Node 17 { 18 int l, r, s; 19 }a[N]; 20 21 void update(int& q, int l, int r, int x) 22 { 23 sum[++tot] = sum[q] + 1; 24 ls[tot] = ls[q]; 25 rs[tot] = rs[q]; 26 q = tot; 27 28 if (l == r) return; 29 30 int mid = (l + r) >> 1; 31 if (x <= mid) update(ls[q], l, mid, x); 32 else update(rs[q], mid + 1, r, x); 33 } 34 35 int query(int lt, int rt, int l, int r, int k) 36 { 37 if (l == r) return l; 38 39 int s = sum[ls[rt]] - sum[ls[lt]]; 40 int mid = (l + r) >> 1; 41 if (k <= s) return query(ls[lt], ls[rt], l, mid, k); 42 else return query(rs[lt], rs[rt], mid + 1, r, k - s); 43 } 44 45 int main() 46 { 47 scanf("%d%d", &n, &m); 48 49 int all = 0; 50 for (int i = 1; i <= n; i++) 51 { 52 scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].s); 53 all = max(all, a[i].r); 54 } 55 56 for (int i = 1; i <= m; i++) 57 { 58 int x; 59 scanf("%d", &x); 60 t[x].push_back(i); 61 all = max(all, x); 62 } 63 64 for (int i = 1; i <= all; i++) 65 { 66 root[i] = root[i - 1]; 67 if (!t[i].empty()) 68 { 69 for (int j = 0; j < t[i].size(); ++j) update(root[i], 1, m, t[i][j]); 70 } 71 } 72 73 for (int i = 1; i <= n; i++) 74 { 75 if (sum[root[a[i].r]] - sum[root[a[i].l - 1]] < a[i].s) continue; 76 int rp = query(root[a[i].l - 1], root[a[i].r], 1, m, a[i].s); 77 ++res[rp]; 78 } 79 for (int i = 1; i <= m; i++) printf("%d\n", res[i]); 80 }
【CF316E3】 Summer Homework (题目):
引理:
fi = fkfi-k + fk-1fi-k-1 (1≤k<i)
证明:
当i≤1时结论成立
当i>1时,假设fi-1和fi-2满足引理,选取一个k(1≤k<i-2)
fi=fi-1+fi-2
fi=fkfi-1-k+fk-1fi-1-k-1+fkfi-2-k+fkfi-2-k-1
fi = fkfi-k + fk-1fi-k-1
之后再s1表示a1*f0+a2*f1+a3*f2 , s2表示a1*f1+a2*f2+a3*f3
若要把这个序列的斐波那契系数向右移动x个单位,设结果为S,则
S=S1*fx-2+S2*fx-1
在线段树上维护S1和S2,之后执行pushup操作时,将右子节点的斐波那契系数向右移动x位(左子节点的长度),就可以合并出他们的父节点了
执行区间修改时,假设该区间都加上了x,则相当于整个区间加上了x倍的一段斐波那契前缀和,预处理前缀和即可
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef long long LL; 8 9 const LL N = 2e5 + 10, mod = 1e9; 10 11 LL n, m; 12 LL f[N], sum[N]; 13 LL s1[N * 4], s2[N * 4], lazy[N * 4]; 14 15 void add(LL q, LL l, LL r, LL x) 16 { 17 lazy[q] = (lazy[q] + x) % mod; 18 s1[q] = (s1[q] + sum[r - l] * x % mod) % mod; 19 s2[q] = (s2[q] + (sum[r - l + 1] + mod - 1) % mod * x % mod) % mod; 20 } 21 22 LL calc(LL q, LL x) 23 { 24 if (x == 0) return s1[q]; 25 else if (x == 1) return s2[q]; 26 else return (s1[q] * f[x - 2] % mod + s2[q] * f[x - 1] % mod) % mod; 27 } 28 29 void pushup(LL q, LL l, LL r) 30 { 31 LL mid = (l + r) >> 1; 32 s1[q] = (s1[q << 1] + calc(q << 1 | 1, mid - l + 1)) % mod; 33 s2[q] = (s2[q << 1] + calc(q << 1 | 1, mid - l + 2)) % mod; 34 } 35 36 void pushdown(LL q, LL l, LL r) 37 { 38 if (!lazy[q]) return; 39 40 LL mid = (l + r) >> 1; 41 add(q << 1, l, mid, lazy[q]); 42 add(q << 1 | 1, mid + 1, r, lazy[q]); 43 lazy[q] = 0; 44 } 45 46 void update(LL q, LL l, LL r, LL L, LL R, LL x) 47 { 48 if (L <= l && r <= R) 49 { 50 add(q, l, r, x); 51 return; 52 } 53 54 pushdown(q, l, r); 55 LL mid = (l + r) >> 1; 56 if (L <= mid) update(q << 1, l, mid, L, R, x); 57 if (mid < R) update(q << 1 | 1, mid + 1, r, L, R, x); 58 pushup(q, l, r); 59 } 60 61 void modify(LL q, LL l, LL r, LL x, LL v) 62 { 63 if (l == r) 64 { 65 s1[q] = s2[q] = v; 66 return; 67 } 68 69 pushdown(q, l, r); 70 LL mid = (l + r) >> 1; 71 if (x <= mid) modify(q << 1, l, mid, x, v); 72 else modify(q << 1 | 1, mid + 1, r, x, v); 73 pushup(q, l, r); 74 } 75 76 LL query(LL q, LL l, LL r, LL L, LL R) 77 { 78 if (L <= l && r <= R) 79 { 80 return calc(q, l - L); 81 } 82 83 pushdown(q, l, r); 84 LL res = 0; 85 LL mid = (l + r) >> 1; 86 if (L <= mid) res = query(q << 1, l, mid, L, R); 87 if (mid < R) res = (res + query(q << 1 | 1, mid + 1, r, L, R)) % mod; 88 return res; 89 } 90 91 void init() 92 { 93 f[0] = f[1] = 1; 94 sum[0] = 1, sum[1] = 2; 95 for (int i = 2; i < N; i++) 96 { 97 f[i] = (f[i - 1] + f[i - 2]) % mod; 98 sum[i] = (sum[i - 1] + f[i]) % mod; 99 } 100 } 101 102 int main() 103 { 104 init(); 105 106 scanf("%lld%lld", &n, &m); 107 for (int i = 1; i <= n; i++) 108 { 109 LL x; 110 scanf("%lld", &x); 111 modify(1, 1, n, i, x); 112 } 113 114 while (m--) 115 { 116 LL c; 117 scanf("%lld", &c); 118 switch (c) 119 { 120 case 1: 121 { 122 LL i, x; 123 scanf("%lld%lld", &i, &x); 124 modify(1, 1, n, i, x); 125 break; 126 } 127 case 2: 128 { 129 LL l, r; 130 scanf("%lld%lld", &l, &r); 131 printf("%lld\n", query(1, 1, n, l, r) % mod); 132 break; 133 } 134 case 3: 135 { 136 LL l, r, x; 137 scanf("%lld%lld%lld", &l, &r, &x); 138 update(1, 1, n, l, r, x); 139 break; 140 } 141 } 142 } 143 }
【NOI2017】 整数 (题目):
将加和减分开统计,比较后缀大小,暴力找到下一个不等的位置。用set维护不等的位置,每次查找以下。
将数字每16个分成一位,每次修改是修改块内是为不同的数字个数,用set维护每一块内是否有不同的数字,每次询问我们先在当前块中查询,如果没有则在上一个有不同数字的块中查找。
1 #include<cstdio> 2 #include<cstring> 3 #include<set> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 30000005, L = 200005; 9 10 char str[L]; 11 char *S = str, * T = str; 12 13 char gc() 14 { 15 if (S == T) 16 { 17 T = (S = str) + fread(str, 1, L, stdin); 18 if (S == T) return EOF; 19 } 20 return *S++; 21 } 22 23 int read() 24 { 25 int x = 0, f = 1; 26 char c = gc(); 27 for (; c < '0' || c>'9'; c = gc()) if (c == '-') f = -1; 28 for (; c >= '0' && c <= '9'; c = gc()) x = (x << 1) + (x << 3) + c - 48; 29 return x * f; 30 } 31 32 int n, x, y; 33 bool fl, a[N], b[N]; 34 35 struct cmp 36 { 37 bool operator ()(const int& x, const int& y) 38 { 39 return x > y; 40 } 41 }; 42 43 struct Node 44 { 45 int cnt[2000005]; 46 set<int, cmp> S; 47 48 Node() 49 { 50 S.insert(-1); 51 memset(cnt, 0, sizeof(cnt)); 52 } 53 54 void insert(int x) 55 { 56 x += 16; 57 if (!cnt[x >> 4]) S.insert(x >> 4); 58 cnt[x >> 4]++; 59 } 60 61 void erase(int x) 62 { 63 x += 16; 64 cnt[x >> 4]--; 65 if (!cnt[x >> 4]) S.erase(x >> 4); 66 } 67 68 int query(int x) 69 { 70 if (x < 0) return -2; 71 x += 16; 72 for (int i = x; i >= ((x >> 4) << 4) && i >= 16; i--) 73 { 74 if (a[i - 16] ^ b[i - 16]) return i - 16; 75 } 76 int y = *S.lower_bound((x >> 4) - 1); 77 if (y == -1) return -2; 78 for (int i = (y << 4) + 15; i >= (y << 4) && i >= 16; i--) 79 { 80 if (a[i - 16] ^ b[i - 16]) return i - 16; 81 } 82 return -2; 83 } 84 }tree; 85 86 void add() 87 { 88 bool tg = 1; 89 for (int i = y; i <= n * 30 && ((tg ^ 1) || x); i++, x /= 2) 90 { 91 if ((tg ^ 1) ^ (x & 1)) 92 { 93 if (a[i] ^ b[i]) tree.erase(i); 94 else tree.insert(i); 95 if (fl) tg = (a[i] ^= 1); 96 else tg = (b[i] ^= 1); 97 } 98 else 99 tg = ((tg ^ 1) && (x & 1)) ^ 1; 100 } 101 } 102 103 int main() 104 { 105 n = read(); 106 read(), read(), read(); 107 108 tree.insert(-2); 109 for (int i = 1; i <= n; i++) 110 { 111 if (read() == 1) 112 { 113 x = read(); y = read(); 114 fl = (x > 0); x = x < 0 ? -x : x; 115 add(); 116 } 117 else 118 { 119 x = read(); y = tree.query(x - 1); 120 putchar((a[x] ^ b[x] ^ (y != -2 && !a[y])) + '0'); 121 puts(""); 122 } 123 } 124 }
【NOI2016】区间(题目):
将区间按长度排序,从小到大枚举,通过线段树判断某个点是否被覆盖m次
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<iostream> 5 #include<algorithm> 6 #include<stack> 7 #include<queue> 8 using namespace std; 9 10 const int N = 501000; 11 const int INF = 1e9; 12 13 int n, m; 14 int res, cur = 0; 15 int L[N * 2], R[N * 2]; 16 int t[N * 8], sum[N * 8]; 17 18 struct Point 19 { 20 int val, ord; 21 }p[N * 4]; 22 23 struct Node 24 { 25 int len, ord; 26 }a[N * 4]; 27 28 bool cmp1(Point x1, Point x2) 29 { 30 if (x1.val < x2.val)return true; 31 return false; 32 } 33 34 bool cmp2(Node x1, Node x2) 35 { 36 if (x1.len < x2.len)return true; 37 return false; 38 } 39 40 void pushdown(int q, int l, int r) 41 { 42 if (!sum[q])return; 43 int ls = q << 1, rs = q << 1 | 1; 44 t[ls] += sum[q]; 45 t[rs] += sum[q]; 46 sum[ls] += sum[q]; 47 sum[rs] += sum[q]; 48 sum[q] = 0; 49 return; 50 } 51 52 void update(int q, int l, int r, int x, int y, int val) 53 { 54 if (x > r || y < l) return; 55 if (x <= l && y >= r) 56 { 57 t[q] += val; 58 sum[q] += val; 59 return; 60 } 61 int mid = (l + r) / 2; 62 63 pushdown(q, l, r); 64 update(q << 1, l, mid, x, y, val); 65 update(q << 1 | 1, mid + 1, r, x, y, val); 66 t[q] = max(t[q << 1], t[q << 1 | 1]); 67 } 68 69 int main() 70 { 71 scanf("%d%d", &n, &m); 72 for (int i = 1; i <= n; i++) 73 { 74 int u, v; 75 scanf("%d%d", &u, &v); 76 a[i].len = v - u; 77 a[i].ord = i; 78 79 cur++; 80 p[cur].val = u; 81 p[cur].ord = i; 82 83 cur++; 84 p[cur].val = v; 85 p[cur].ord = i; 86 } 87 88 sort(p + 1, p + cur + 1, cmp1); 89 int num = 0; 90 p[0].val = -1; 91 for (int i = 1; i <= cur; i++) 92 { 93 if (p[i].val != p[i - 1].val) num++; 94 95 int u = p[i].ord; 96 if (!L[u]) L[u] = num; 97 else R[u] = num; 98 } 99 100 res = num; 101 sort(a + 1, a + n + 1, cmp2); 102 int ans = INF, le = 0, ri = 0; 103 while (true) 104 { 105 while (t[1] < m && ri <= n) 106 { 107 ri++; 108 int u = a[ri].ord, v = L[u], w = R[u]; 109 update(1, 1, res, v, w, 1); 110 } 111 112 if (t[1] < m)break; 113 while (t[1] >= m && le <= n) 114 { 115 le++; 116 int u = a[le].ord, v = L[u], w = R[u]; 117 update(1, 1, res, v, w, -1); 118 } 119 ans = min(ans, a[ri].len - a[le].len); 120 } 121 122 if (ans == INF) printf("-1\n"); 123 else printf("%d\n", ans); 124 }
Preprefix sum(题目):
因为 所以 SSi =拆开化简为 SSi = SSi = ,用线段树维护aj和aj*j两个值即可
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef long long LL; 8 typedef pair<LL, LL> PLL; 9 10 const int N = 100010; 11 12 int n, m; 13 struct Node 14 { 15 long long s1, s2; 16 }t[N*4]; 17 18 PLL add(PLL a, PLL b) 19 { 20 return make_pair(a.first + b.first, a.second + b.second); 21 } 22 23 void pushup(int q, int l, int r) 24 { 25 t[q].s1 = t[q << 1].s1 + t[q << 1 | 1].s1; 26 t[q].s2 = t[q << 1].s2 + t[q << 1 | 1].s2; 27 } 28 29 void modify(int q, int l, int r, int x, int v) 30 { 31 if (l == r) 32 { 33 t[q].s1 = (LL) v; 34 t[q].s2 = (LL) v * x; 35 return; 36 } 37 38 int mid = (l + r) >> 1; 39 if (x <= mid) modify(q << 1, l, mid, x, v); 40 else if (x > mid) modify(q << 1 | 1, mid + 1, r, x, v); 41 pushup(q, l, r); 42 } 43 44 PLL query(int q, int l, int r, int x) 45 { 46 if (r <= x) 47 { 48 return make_pair(t[q].s1, t[q].s2); 49 } 50 51 int mid = (l + r) >> 1; 52 if (x <= mid) return query(q << 1, l, mid, x); 53 else return add(query(q << 1, l, mid, x), query(q << 1 | 1, mid + 1, r, x)); 54 } 55 56 int main() 57 { 58 scanf("%d%d", &n, &m); 59 for (int i = 1; i <= n; i++) 60 { 61 int x; 62 scanf("%d", &x); 63 modify(1, 1, n, i, x); 64 } 65 66 while (m--) 67 { 68 char qu[10]; 69 scanf("%s", qu); 70 if (qu[0] == 'Q') 71 { 72 int r; 73 scanf("%d", &r); 74 PLL res = query(1, 1, n, r); 75 printf("%lld\n", res.first * (r + 1) - res.second); 76 } 77 else 78 { 79 int i, x; 80 scanf("%d%d", &i, &x); 81 modify(1, 1, n, i, x); 82 } 83 } 84 }
【ZJOI2013】k大数查询(题目):
树状数组套线段树,树状数组的节点表示编号在【l,r】之间的集合,线段树的节点表示值在【l,r】范围的点有多少
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 typedef long long LL; 9 10 const int N = 5000010; 11 12 LL n, m; 13 LL tot, a[N]; 14 LL son[N][2], lazy[N], sum[N]; 15 16 LL lowbit(LL x) 17 { 18 return x & -x; 19 } 20 21 LL LB(LL x) 22 { 23 return 1ll << (LL)(log2(x)); 24 } 25 26 LL q(LL& x) 27 { 28 if (x == 0) x = ++tot; 29 return x; 30 } 31 32 void update(LL L, LL R, LL l, LL r, LL nod) 33 { 34 int mid = ((L + R) >> 1); 35 sum[nod] += r - l + 1; 36 37 if (L == l && R == r) lazy[nod]++; 38 else if (r <= mid) update(L, mid, l, r, q(son[nod][0])); 39 else if (l > mid) update(mid + 1, R, l, r, q(son[nod][1])); 40 else update(L, mid, l, mid, q(son[nod][0])), update(mid + 1, R, mid + 1, r, q(son[nod][1])); 41 } 42 43 LL query(LL L, LL R, LL l, LL r, LL nod) 44 { 45 LL mid = ((L + R) >> 1); 46 if (L == l && R == r) return sum[nod]; 47 else if (r <= mid) return lazy[nod] * (r - l + 1) + (son[nod][0] == 0 ? 0 : query(L, mid, l, r, son[nod][0])); 48 else if (l > mid) return lazy[nod] * (r - l + 1) + (son[nod][1] == 0 ? 0 : query(mid + 1, R, l, r, son[nod][1])); 49 else return lazy[nod] * (r - l + 1) + (son[nod][0] == 0 ? 0 : query(L, mid, l, mid, son[nod][0])) + (son[nod][1] == 0 ? 0 : query(mid + 1, R, mid + 1, r, son[nod][1])); 50 } 51 52 signed main() 53 { 54 scanf("%lld%lld", &n, &m); 55 tot = n; 56 while (m--) 57 { 58 LL opt, l, r, c; 59 scanf("%lld%lld%lld%lld", &opt, &l, &r, &c); 60 if (2 - opt) 61 { 62 for (int i = n - c + 1; i <= n; i += lowbit(i)) 63 { 64 update(1, n, l, r, i); 65 } 66 } 67 else 68 { 69 LL num = 0, ans = 0; 70 for (int i = LB(n); i != 0; i >>= 1) 71 { 72 if (ans + i > n) continue; 73 LL F = query(1, n, l, r, ans + i) + num; 74 if (F < c) ans += i, num = F; 75 } 76 ans++; 77 printf("%lld\n", n - ans + 1); 78 } 79 } 80 }