zoukankan      html  css  js  c++  java
  • 猫树 简单介绍

    猫树是一种类似ST表的数据结构,初始化 (O(nlog n)) 查询 (O(1)),不允许修改(或者说修改代价非常大)

    ST表支持的运算要满足可重复贡献,即 (x imes x=x),比如区间RMQ、区间gcd、区间lcm等

    前缀和支持的运算要满足存在逆元(意味着存在逆运算),比如区间和、区间模乘等

    而猫树则没有两个限制,猫树支持线段树支持的几乎所有查询操作,除去上述操作外还可以支持矩阵乘法、线性基(ST表也可以线性基但是不能判断是否线性相关)等

    我们以ST表模板题为例介绍一下猫树
    https://www.luogu.com.cn/problem/P3865

    我们先假设n是2的整数次幂,如果不是就要在初始化的时候处理一下

    猫树的第0层所有 ([i,i]) 构成了 (n) 个区间,第1层所有 ([2i,2i+1]) 构成了 (dfrac n 2) 区间,第2层所有 ([4i,4i+3]) 构成了 (dfrac n 4) 个区间,依次类推

    假设第 (s) 层有一个区间 ([l,r]),令它的中点 (m=(l+r)/2),计算区间 ([l,m]) 的后缀和,存在 (cat[s][l...m]) 中;同样计算区间 ([m+1,r]) 的前缀和,存在 (cat[s][(m+1)...r])

    比如 (n=8) 的情况如下

    原数组 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]
    第0层 [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]
    第1层 [ ] [ ] [ ] [ ]
    第2层 [ - - ] [ - - ]
    第3层 [ - - - - - - ]
    原数组 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]
    第0层 (a[0]) (a[1]) (a[2]) (a[3]) (a[4]) (a[5]) (a[6]) (a[7])
    第1层 (a[0]) (a[1]) (a[2]) (a[3]) (a[4]) (a[5]) (a[6]) (a[7])
    第2层 (a[0]+a[1]) (a[1]) (a[2]) (a[2]+a[3]) (a[4]+a[5]) (a[5]) (a[6]) (a[6]+a[7])
    第3层 (a[0]+...+a[3]) (a[1]+...+a[3]) (a[2]+a[3]) (a[3]) (a[4]) (a[4]+a[5]) (a[4]+...+a[6]) (a[4]+...+a[7])

    我们发现对任意的查询区间 ([l_q,r_q]),一定存在猫树上的某个区间 ([l,r])([l,r]) 包含了 ([l_q,r_q]) 并且它的中点在 ([l_q,r_q])

    因此我们只要找到这个区间,然后用后缀和+前缀和来计算答案

    因为某种巧合,区间 ([l_q,r_q]) 对应的猫树区间在第 (lfloorlog_2(l_q)^(r_q) floor) 层内

    这里有个小操作,可以用 32-__builtin_clz(x) 代替 (lfloorlog_2(x) floor)因为我懒得初始化log数组

    猫树的代码量其实还好,也就st表的两三倍

    #include <bits/stdc++.h>
    using namespace std;
    #define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
    #define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
    typedef long long ll; const int inf=~0u>>2; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
    const int N=200010;
    ll in[N];
    struct cat{
    	#define U(a,b) max(a,b) //查询操作
    	#define a0 0 //查询操作的零元
    	#define logN 21
    	ll a[logN][N]; //内存等于2^k且大于等于两倍inn
    	void init(int s,int l,int r){
    		int m=(l+r)/2;
    		repeat_back(i,l,m)a[s][i]=U(a[s][i+1],in[i]);
    		repeat(i,m+2,r+1)a[s][i]=U(a[s][i-1],in[i]);
    	}
    	void init(int inn){ //建树
    		int n; for(n=1;n<inn;n<<=1); repeat(i,inn,n)in[i]=a0;
    		for(int len=1,s=0;len<=n;len<<=1,s++){
    			repeat(i,0,n)a[s][i]=in[i];
    			for(int i=0;i<n;i+=len)
    				init(s,i,i+len-1);
    		}
    	}
    	ll query(int l,int r){ //区间查询
    		int s=32-__builtin_clz(l^r);
    		if(l==r)return a[s][l];
    		else return U(a[s][l],a[s][r]);
    	}
    }tr;
    signed main(){
    	//ios::sync_with_stdio(0); cin.tie(0); //freopen("in.txt","r",stdin);
    	int n=read(),m=read();
    	repeat(i,0,n)in[i]=read();
    	tr.init(n);
    	while(m--){
    		int x=read()-1,y=read()-1;
    		printf("%lld
    ",tr.query(x,y));
    	}
    	return 0;
    }
    
  • 相关阅读:
    三级菜单python写法(递归写法)
    webstorm2018.1.6版本安装+破解+汉化
    sourceTree 的使用教程
    nodeppt的使用教程
    堆和栈的区别(转过无数次的文章)
    黎曼滤波在神经计算方面的应用
    深度学习笔记——PCA原理与数学推倒详解
    TCP/IP模型详解
    OSI7层模型详解
    CNN车型分类总结
  • 原文地址:https://www.cnblogs.com/axiomofchoice/p/12519293.html
Copyright © 2011-2022 走看看