O(1) 的复杂度求一段区间的 gcd" role="presentation">gcd ,我们这时就不能用线段树或ST表了,就可" />
先介绍一下猫树的创始人——immortalCO,一位巨犇。
当我们遇到这样一道题——要以 O(1)" role="presentation">O(1) 的复杂度求一段区间的 gcd" role="presentation">gcd ,我们这时就不能用线段树或ST表了,就可以使用猫树。
猫树的基本思想为,将询问的区间分为两个已经预处理好的,可以 O(1)" role="presentation">O(1) 查询的区间。
就像线段树一样,猫树的会把一段区间分成左右两段,从中点处分别向两边遍历,(注意:左半边的遍历是倒序的),然后记录每一次的结果,复杂度: O(nlogn)" role="presentation">O(nlogn) 。
用求 gcd" role="presentation">gcd 的一段代码来演示这个操作 ↓" role="presentation">↓
build(num << 1, l, mid, dep + 1); //build是建树函数,用递归实现。 build(num << 1 | 1, mid + 1, r, dep + 1); //num是此时的编号,dep是此时的深度 dp[mid][dep] = a[mid]; //初始化左半边的起点 for (int i = mid - 1; i >= l; --i) { //注意:左半边的遍历是倒序的 dp[i][dep] = a[i]; dp[i][dep] = std::__gcd(dp[i][dep], dp[i + 1][dep]); //与下一位比较 } dp[mid + 1][dep] = a[mid + 1]; //初始化右半边的起点 for (int i = mid + 2; i <= r; ++i) { dp[i][dep] = a[i]; dp[i][dep] = std::__gcd(dp[i][dep], dp[i - 1][dep]); }
在查询时,我们会找到一个能把要查询的区间分成两部分的 mid" role="presentation">mid 然后 实现 O(1)" role="presentation">O(1) 查询。
那么,如何找到那个 mid" role="presentation">mid 呢?
我们发现,每一个深度都有若干个 mid" role="presentation">mid 而且,只要我们找到了正确的 mid" role="presentation">mid 所在的深度,我们就可以直接用 gcd(dp[l][dep],dp[r][dep])" role="presentation">gcd(dp[l][dep],dp[r][dep]) 就可以查询了。
那么,如何找到那个深度呢。
我们发现,对于任意的两个 l,r" role="presentation">l,r ,能把他们分割成两块的那个点所在的区间一定是 l,r" role="presentation">l,r 在树上的 LCA" role="presentation">LCA ,如何求呢。
因为,猫树每次一段区间分成两块,所以,树上两点的 LCA" role="presentation">LCA ,即为,他们编号的最长公共前缀。
So,我们就可以记录每个数字在二进制下的长度,并记录每一个下标所对应的树上编号,这样,就可以做到 O(1)" role="presentation">O(1) 查询了。
code:
#pragma GCC optimize(3, "Ofast", "inline") #include <iostream> #include <cassert> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <map> #include <cmath> #include <queue> #include <set> #include <climits> #include <random> #include <bitset> #include <unordered_map> #define orz % #define ll long long #define juruo Optimist_Skm #define mid ((l + r) >> 1) #define pii std::pair<int, int> #define fi first #define se second #define eb emplace_back #define pb push_back #define m_p std::make_pair #define pq std::priority_queue<int> #define pq_min std::priority_queue<int, std::vector<int>, std::greater<int> > #define open(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout); #define test(x) cout << "Test: " << x << 'n' #define close fclose(stdin);fclose(stdout); #define ull unsigned long long #define debug(); printf("qwqn"); namespace Fast_Skm {template <typename T>inline void read(T &x) {T s = 0, w = 1;char c = getchar();while(!isdigit(c)) {if(c == '-') w = -1;c = getchar();}while(isdigit(c))s = (s << 1) + (s << 3) + (c & 0xcf), c = getchar();x = s * w;return ;}template <typename T, typename... Arp>inline void read(T &x, Arp &...arp) {read(x), read(arp...); return ;}template <typename T>inline void write(T x) {if(x < 0) x = -x, putchar('-');int p = 0;static char s[100];do {s[++p] = x orz 10 + '0';x /= 10;} while (x);while(p) putchar(s[p--]);putchar('n');}template <typename T, typename... Arp>inline void write(T &x, Arp &...arp) {write(x), write(arp...);return ;}template <typename S, typename T>inline void smax(S &x, T y) {x = (x > y) ? x : y;}template <typename S, typename T>inline void smin(S &x, T y) {x = (x < y) ? x : y;}inline void quit() {exit(0);return ;}inline ll quick_pow(ll a, ll b, ll P) {ll ans = 1;while(b >= 1) {if(b & 1) {ans = ans * a % P;}a = a * a % P;b >>= 1;}return ans;}template <typename T>inline T exgcd(T a, T b, T &x, T &y) {if(b == 0) {x = 1; y = 0;return a;}int gcd = exgcd(b, a % b, x, y);int tmp = y;y = x - a / b * y;x = tmp;return gcd;} } using namespace Fast_Skm; const int N = 5e5 + 5, M = 25; int a[N], dp[N][M], n, m, lg[N], pos[N]; inline void build(int num, int l, int r, int dep) {if (l == r) {pos[r] = num;return ;}build(num << 1, l, mid, dep + 1);build(num << 1 | 1, mid + 1, r, dep + 1);dp[mid][dep] = a[mid];for (int i = mid - 1; i >= l; --i) {dp[i][dep] = a[i];dp[i][dep] = std::__gcd(dp[i][dep], dp[i + 1][dep]);}dp[mid + 1][dep] = a[mid + 1];for (int i = mid + 2; i <= r; ++i) {dp[i][dep] = a[i];dp[i][dep] = std::__gcd(dp[i][dep], dp[i - 1][dep]);}return ; } signed main() {std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);read(n, m);for (int i = 1; i <= n; ++i) {read(a[i]);}int len = 1;while(len < n) len <<= 1; //此处的程度并不是n,而是大于n的第一个的2的次幂。build(1, 1, len, 1);lg[1] = 0; lg[0] = 0;for (int i = 1; i <= len << 1; ++i) {lg[i] = lg[i >> 1] + 1; //预处理出每一个数在二进制下的长度}for (int i = 1; i <= m; ++i) {int l, r;read(l, r);if (l == r) { //此处必须特判write(a[l]);continue;}int p = lg[pos[r]] - (lg[pos[r] ^ pos[l]]); //计算 LCA 的深度write(std::__gcd(dp[l][p], dp[r][p]));}return 0; }
洛谷P3685
相关知识
数据结构自学日志——猫树
数据结构入门——猫树
谈一类神奇的数据结构——猫树
数据结构 猫树
猫树 简单介绍
猫树 学习笔记
「猫树」猫树公司黄页
DIY猫树,简易猫爬架,简易猫树,猫树。#造型猫树 #简易猫
自学宠物美容的方法
【猫树】
网址: 数据结构自学日志——猫树 https://m.mcbbbk.com/newsview661286.html
上一篇: 珑小宠 猫爬架猫窝加粗大型猫树一 |
下一篇: 为什么猫咪上树不下树 |