1.分块

大概就是把序列分成若干块,预处理出一些东西,整块打标记,边角暴力。

当然不一定是对着序列分块。可能对着任何东西分块,比如哈希冲突...

复杂度随着题目相应变化,一般用均值不等式来确定块的大小。

例题首先是hzwer的分块9题

说两个神奇的分块思想。

1.当暴力复杂度是n²m,且每个元素贡献独立的时候,可以把这些元素分成n0.5块,分别暴力。

总复杂度是n1.5m(有用吗...)

2.对时间/操作分块。

把操作分成若干块,块大小是T。之后对于每一块,预处理之前的影响,暴力查块内的影响。

总复杂度是O(预处理复杂度 * T + m² / T * 暴力查询复杂度)

2.莫队

先%%%莫涛。

两个指针跳来跳去......

普通莫队:就是离线,把询问排序,使得挪动指针的总次数不大于n1.5

例题:小Z的袜子 小B的询问  大爷的字符串  小清新人渣的本愿 

带修莫队:三个维度排序。时间作为第三关键字。

相当于三个指针跳来跳去,第三个指针在时间上。这样就懂了吧。

例题:洛谷P1903 [国家集训队]数颜色 / 维护队列

树上莫队:用括号序列转成序列莫队。

先讲一下括号序列,DFS序,欧拉序的区别。

  • DFS序就是DFS时,第一次进入点时将其加入序列,长度为n。树剖就是用的一种DFS序。
  • 括号序列:出来的时候额外加入一次,长度为2n。
  • 欧拉序:每次切换节点时,将当前节点加入序列。长度为2n - 1,因为每个非根节点都会自己加入一次,让父亲加入一次。

把括号序列搞出来。我们统计其中出现一次的点。

对于路径x - y的询问,先让first[x] < first[y]

求lca:z。如果z == x,那么直接用first[x] - first[y]

否则就用last[x] - first[y],然后查询时加上z。

此处的first和last表示第一,二次在序列中出现的位置。

例题:SP10707 Count on a tree II

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cmath>
  4 
  5 const int N = 200010;
  6 
  7 struct Edge {
  8     int nex, v;
  9 }edge[N << 1]; int top;
 10 
 11 int fr[N], e[N], a[N], first[N], last[N], num, val[N], X[N], fa[N][20], pw[N], d[N], n, ans, bin[N];
 12 bool vis[N];
 13 
 14 struct ASK {
 15     int l, r, t, ans, ex;
 16     inline bool operator <(const ASK &w) const {
 17         if(fr[l] != fr[w.l]) {
 18             return l < w.l;
 19         }
 20         return r < w.r;
 21     }
 22 }ask[N];
 23 
 24 inline bool cmp(const ASK &A, const ASK &B) {
 25     return A.t < B.t;
 26 }
 27 
 28 inline void add(int x, int y) {
 29     top++;
 30     edge[top].v = y;
 31     edge[top].nex = e[x];
 32     e[x] = top;
 33     return;
 34 }
 35 
 36 void DFS(int x, int f) {
 37     a[++num] = x;
 38     first[x] = num;
 39     fa[x][0] = f;
 40     d[x] = d[f] + 1;
 41     for(int i = e[x]; i; i = edge[i].nex) {
 42         int y = edge[i].v;
 43         if(y == f) {
 44             continue;
 45         }
 46         DFS(y, x);
 47     }
 48     a[++num] = x;
 49     last[x] = num;
 50     return;
 51 }
 52 
 53 inline int lca(int x, int y) {
 54     if(d[x] > d[y]) {
 55         std::swap(x, y);
 56     }
 57     int t = pw[n];
 58     while(t >= 0 && d[x] != d[y]) {
 59         if(d[fa[y][t]] >= d[x]) {
 60             y = fa[y][t];
 61         }
 62         t--;
 63     }
 64     if(x == y) {
 65         return x;
 66     }
 67     t = pw[n];
 68     while(t >= 0 && fa[x][0] != fa[y][0]) {
 69         if(fa[x][t] != fa[y][t]) {
 70             x = fa[x][t];
 71             y = fa[y][t];
 72         }
 73         t--;
 74     }
 75     return fa[x][0];
 76 }
 77 
 78 inline void add(int x) {
 79     if(!bin[val[x]]) {
 80         ans++;
 81     }
 82     bin[val[x]]++;
 83     return;
 84 }
 85 
 86 inline void del(int x) {
 87     bin[val[x]]--;
 88     if(!bin[val[x]]) {
 89         ans--;
 90     }
 91     return;
 92 }
 93 
 94 inline void work(int p) {
 95     int x = a[p];
 96     if(vis[x]) {
 97         del(x);
 98     }
 99     else {
100         add(x);
101     }
102     vis[x] ^= 1;
103     return;
104 }
105 
106 int main() {
107     int m;
108     scanf("%d%d", &n, &m);
109     for(int i = 2; i <= n; i++) {
110         pw[i] = pw[i >> 1] + 1;
111     }
112     int T = sqrt(n << 1);
113     for(int i = 1; i <= n; i++) {
114         scanf("%d", &val[i]);
115         X[i] = val[i];
116     }
117     std::sort(X + 1, X + n + 1);
118     int temp = std::unique(X + 1, X + n + 1) - X - 1;
119     for(int i = 1; i <= n; i++) {
120         val[i] = std::lower_bound(X + 1, X + temp + 1, val[i]) - X;
121     }
122     for(int i = 1, x, y; i < n; i++) {
123         scanf("%d%d", &x, &y);
124         add(x, y);
125         add(y, x);
126     }
127     DFS(1, 0);
128     for(int j = 1; j <= pw[n]; j++) {
129         for(int i = 1; i <= n; i++) {
130             fa[i][j] = fa[fa[i][j - 1]][j - 1];
131         }
132     }
133     for(int i = 1; i <= n * 2; i++) {
134         fr[i] = (i - 1) / T + 1;
135     }
136     for(int i = 1, x, y; i <= m; i++) {
137         scanf("%d%d", &x, &y);
138         if(first[x] > first[y]) {
139             std::swap(x, y);
140         }
141         int z = lca(x, y);
142         if(z == x) {
143             ask[i].l = first[x];
144             ask[i].r = first[y];
145         }
146         else {
147             ask[i].l = last[x];
148             ask[i].r = first[y];
149             ask[i].ex = z;
150         }
151         ask[i].t = i;
152     }
153 
154     std::sort(ask + 1, ask + m + 1);
155     int l = 1, r = 1;
156     work(1);
157 
158     for(int i = 1; i <= m; i++) {
159         while(l > ask[i].l) {
160             l--;
161             work(l);
162         }
163         while(r < ask[i].r) {
164             r++;
165             work(r);
166         }
167         while(l < ask[i].l) {
168             work(l);
169             l++;
170         }
171         while(r > ask[i].r) {
172             work(r);
173             r--;
174         }
175         if(ask[i].ex) {
176             add(ask[i].ex);
177         }
178         ask[i].ans = ans;
179         if(ask[i].ex) {
180             del(ask[i].ex);
181         }
182     }
183 
184     std::sort(ask + 1, ask + m + 1, cmp);
185     for(int i = 1; i <= m; i++) {
186         printf("%d\n", ask[i].ans);
187     }
188     return 0;
189 }
AC代码

相关文章: