Loading docs/ds/divide-combine.md +21 −51 Original line number Diff line number Diff line Loading @@ -178,46 +178,12 @@ const int N = 200010; int n, m, a[N], st1[N], st2[N], tp1, tp2, rt; int L[N], R[N], M[N], id[N], cnt, typ[N], bin[20], st[N], tp; char gc() { static char *p1, *p2, s[1000000]; if (p1 == p2) p2 = (p1 = s) + fread(s, 1, 1000000, stdin); return (p1 == p2) ? EOF : *p1++; } int rd() { int x = 0; char c = gc(); while (c < '0' || c > '9') c = gc(); while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = gc(); return x; } char ps[1000000], *pp = ps; void flush() { fwrite(ps, 1, pp - ps, stdout); pp = ps; } void push(char x) { if (pp == ps + 1000000) flush(); *pp++ = x; } void write(int l, int r) { static int sta[N], top; if (!l) push('0'); else { while (l) sta[++top] = l % 10, l /= 10; while (top) push(sta[top--] ^ '0'); } push(' '); if (!r) push('0'); else { while (r) sta[++top] = r % 10, r /= 10; while (top) push(sta[top--] ^ '0'); } push('\n'); } //本篇代码原题应为 CERC2017 Intrinsic Interval //a数组即为原题中对应的排列 //st1和st2分别两个单调栈,tp1、tp2为对应的栈顶,rt为析合树的根 //L、R数组表示该析合树节点的左右端点,M数组的作用在析合树构造时有提到 //id存储的是排列中某一位置对应的节点编号,typ用于标记析点还是合点 //st为存储析合树节点编号的栈,tp为其栈顶 struct RMQ { // 预处理 RMQ(Max & Min) int lg[N], mn[N][17], mx[N][17]; void chkmn(int& x, int y) { Loading Loading @@ -278,7 +244,7 @@ struct SEG { // 线段树 return query(ls, l, mid); else return query(rs, mid + 1, r); // 如果不存在 0 的位置就会自动返回一个极大值 // 如果不存在 0 的位置就会自动返回当前你查询的位置 } } T; Loading @@ -289,7 +255,6 @@ struct Edge { void add(int u, int v) { // 树结构加边 E[o] = (Edge){v, hd[u]}; hd[u] = o++; // printf("%d %d\n",u,v); } void dfs(int u) { for (int i = 1; bin[i] <= dep[u]; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1]; Loading Loading @@ -349,6 +314,7 @@ void build() { } else if (judge(L[st[tp]], i)) { typ[++cnt] = 1; // 合点一定是被这样建出来的 L[cnt] = L[st[tp]], R[cnt] = i, M[cnt] = L[now]; //这里M数组的作用是保证合点的儿子排列是单调的 add(cnt, st[tp--]), add(cnt, now); now = cnt; } else { Loading @@ -375,22 +341,26 @@ void query(int l, int r) { int z = lca(x, y); if (typ[z] & 1) l = L[go(x, dep[x] - dep[z] - 1)], r = R[go(y, dep[y] - dep[z] - 1)]; //合点这里特判的原因是因为这个合点不一定是最小的包含l,r的连续段. //具体可以在上面的例图上试一下查询7,10 else l = L[z], r = R[z]; write(l, r); printf("%d %d\n",l,r); } // 分 lca 为析或和,这里把叶子看成析的 int main() { freopen("c.in", "r", stdin); freopen("c.out", "w", stdout); n = rd(); for (int i = 1; i <= n; ++i) a[i] = rd(); scanf("%d",&n); for (int i = 1; i <= n; ++i) scanf("%d",&a[i]); D.build(); build(); dfs(rt); m = rd(); for (int i = 1; i <= m; ++i) query(rd(), rd()); return flush(), 0; scanf("%d",&m); for (int i = 1; i <= m; ++i) { int x,y; scanf("%d%d",&x,&y); query(x,y); } return 0; } // 20190612 // 析合树 Loading Loading
docs/ds/divide-combine.md +21 −51 Original line number Diff line number Diff line Loading @@ -178,46 +178,12 @@ const int N = 200010; int n, m, a[N], st1[N], st2[N], tp1, tp2, rt; int L[N], R[N], M[N], id[N], cnt, typ[N], bin[20], st[N], tp; char gc() { static char *p1, *p2, s[1000000]; if (p1 == p2) p2 = (p1 = s) + fread(s, 1, 1000000, stdin); return (p1 == p2) ? EOF : *p1++; } int rd() { int x = 0; char c = gc(); while (c < '0' || c > '9') c = gc(); while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = gc(); return x; } char ps[1000000], *pp = ps; void flush() { fwrite(ps, 1, pp - ps, stdout); pp = ps; } void push(char x) { if (pp == ps + 1000000) flush(); *pp++ = x; } void write(int l, int r) { static int sta[N], top; if (!l) push('0'); else { while (l) sta[++top] = l % 10, l /= 10; while (top) push(sta[top--] ^ '0'); } push(' '); if (!r) push('0'); else { while (r) sta[++top] = r % 10, r /= 10; while (top) push(sta[top--] ^ '0'); } push('\n'); } //本篇代码原题应为 CERC2017 Intrinsic Interval //a数组即为原题中对应的排列 //st1和st2分别两个单调栈,tp1、tp2为对应的栈顶,rt为析合树的根 //L、R数组表示该析合树节点的左右端点,M数组的作用在析合树构造时有提到 //id存储的是排列中某一位置对应的节点编号,typ用于标记析点还是合点 //st为存储析合树节点编号的栈,tp为其栈顶 struct RMQ { // 预处理 RMQ(Max & Min) int lg[N], mn[N][17], mx[N][17]; void chkmn(int& x, int y) { Loading Loading @@ -278,7 +244,7 @@ struct SEG { // 线段树 return query(ls, l, mid); else return query(rs, mid + 1, r); // 如果不存在 0 的位置就会自动返回一个极大值 // 如果不存在 0 的位置就会自动返回当前你查询的位置 } } T; Loading @@ -289,7 +255,6 @@ struct Edge { void add(int u, int v) { // 树结构加边 E[o] = (Edge){v, hd[u]}; hd[u] = o++; // printf("%d %d\n",u,v); } void dfs(int u) { for (int i = 1; bin[i] <= dep[u]; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1]; Loading Loading @@ -349,6 +314,7 @@ void build() { } else if (judge(L[st[tp]], i)) { typ[++cnt] = 1; // 合点一定是被这样建出来的 L[cnt] = L[st[tp]], R[cnt] = i, M[cnt] = L[now]; //这里M数组的作用是保证合点的儿子排列是单调的 add(cnt, st[tp--]), add(cnt, now); now = cnt; } else { Loading @@ -375,22 +341,26 @@ void query(int l, int r) { int z = lca(x, y); if (typ[z] & 1) l = L[go(x, dep[x] - dep[z] - 1)], r = R[go(y, dep[y] - dep[z] - 1)]; //合点这里特判的原因是因为这个合点不一定是最小的包含l,r的连续段. //具体可以在上面的例图上试一下查询7,10 else l = L[z], r = R[z]; write(l, r); printf("%d %d\n",l,r); } // 分 lca 为析或和,这里把叶子看成析的 int main() { freopen("c.in", "r", stdin); freopen("c.out", "w", stdout); n = rd(); for (int i = 1; i <= n; ++i) a[i] = rd(); scanf("%d",&n); for (int i = 1; i <= n; ++i) scanf("%d",&a[i]); D.build(); build(); dfs(rt); m = rd(); for (int i = 1; i <= m; ++i) query(rd(), rd()); return flush(), 0; scanf("%d",&m); for (int i = 1; i <= m; ++i) { int x,y; scanf("%d%d",&x,&y); query(x,y); } return 0; } // 20190612 // 析合树 Loading