窦 tree 和一个常数巨大的 O((n+q)log*n) 实现

以下三个段落是碎碎念。

以前是打算一个月写一篇来着,不过这个月现在为止还没写过什么东西(其实之前写过一篇的,不过发出来几个小时后就被 Itst 大爷认出在 CF 上有原题,所以那个模型就一点价值都没有了,现在已经被我隐藏掉了)。

实在想不出来写点啥好了,整个已经被整烂了的活。这玩意已经被发明过八百次了,而且每次发明者起的名都不一样。窦 tree 是某位学长发明这玩意时起的名字。至于那位学长是谁……emm,懂的都懂……毕竟每篇都提一次他是谁的话感觉像是在碰瓷。

讲真这玩意没啥实用价值,但我实在想不出来还能整什么别的活了那咋办嘛。如果这几天想到了其他活的话就把这篇删掉。


下面步入正题,静态区间结合律信息查询。

基本上就是照搬学长博客。

两种比较具有实用价值的做法

第一种是正常的分治数据结构,在每个结点处理左侧后缀信息,右侧前缀信息。序列长度补到 \(2\) 的整数次幂可以做到 \(O(n\log n)-O(1)\)。今天找了找出处,可能是在 这里 被最早提出来的,感觉猫树这个名字蛮可爱。

第二种是按根号递归下去,处理同一层块间信息是 \(O(n)\) 的,可以做到 \(O(n\log\log n)-O(\log\log n)\),学长课件上说这个东西找 LCA 的过程也能优化,但我不会。

窦 tree

低配版:每一层按照 \(\log n\) 分块,块间信息可以用上面的第一种结构来维护,块内递归。这样每一层预处理复杂度是 \(O(n)\),算上查询以后总复杂度是 \(O((n+q)\log^*n)\)

以下两段话的内容当放屁处理,之前写的什么东西,这理解的完全不对
以下两段话的内容当放屁处理,之前写的什么东西,这理解的完全不对
以下两段话的内容当放屁处理,之前写的什么东西,这理解的完全不对
以下两段话的内容当放屁处理,之前写的什么东西,这理解的完全不对
以下两段话的内容当放屁处理,之前写的什么东西,这理解的完全不对

升级版:设第 \(m\) 层数据规模为 \(n\) 时当前层数据结构的深度为 \(T(m,n)\),我们分出的块长为 \(B(m,n)\),那么我们令 \(B(m,n)=T(m-1,n)\),块间信息用上一层数据结构来维护(大致可以理解为对每层的块间信息做 \(\log^*\) 的窦 tree)。块内仍旧递归。

复杂度分析:这样迭代下去,有边界 \(B(1,n)=\log n,T(1,n)=\log^*n\),递推式 \(B(m,n)=T(m-1,n),T(m,n)=T(m,\frac{n}{B(m,n)})+1\)。这个东西降低到 \(O(1)\) 所需的层数我不会算,但学长说查表能发现是 \(\alpha(n)\),写个代码算了算好像确实是,那就是吧。对于预处理,第 \(m\) 层的时间开销是 \(O(nT(m,n))\),下一层的规模约为 \(O(nT(m,n)/T(m−1,n))\)(这个东西只是感性上的估计,我不会算,学长也没有给出更准确的规模),于是总时间复杂度为 \(O(n\alpha(n))\)。对于询问,每一层时间都是 \(O(1)\),因此单次 \(O(\alpha(n))\)

然后说说今天下午 LGMwzs 告诉我的 这玩意另外一次被发明的地方。这个老哥的理解方式蛮有趣的,在 LGMwzs 的教导下,我意识到他说的对树分层和学长说的对序列分块本质上是相同的,把这个老哥说的三次分层改改就能得到一棵窦 tree 状物。

顺便提一嘴,当初学长把这个东西命名为“窦 tree”,是为了纪念机房 2019 级伟大主席窦楠楠的英明领导(下文代码中的结构体名 dnn 即“窦楠楠”之意)。


下面是代码实现。我今天试着写了 \(O((n+q)\log^* n)\) 的低配版窦 tree。这玩意相对来说不那么难写,但完整的窦 tree 就感觉不大可写……有没有人愿意试试?

我的实现方式非常之丑,开了巨大多的 vector 和辅助变量,再加上本来窦 tree 常数也不小,所以跑得非常之慢(测了一下,打不过线段树 /狂笑)。当然我也没打算在实际做题时用这玩意,实现它也是作为码力复健的练习。

不过这玩意访问结点的数量确实比线段树要少得多,\(n=5e5\) 时随机数据下线段树单次查询的最高访问结点数达到了 \(68\),而窦 tree 只有 \(16\)

下面是一个板子,以多次查询区间最大子段和为例。话说我应该没写出锅吧……?

upd:发明窦 tree 的学长本人也写了一下,他的实现比我好看很多,但也使用了更多的 vector,在不开 O2 的情况下同样打不过线段树,但开了之后就跑得飞快(\(n=1e6,q=5e6\),线段树本地 6s,窦 tree 本地 4s),因此写法好的话窦 tree 的优势应该还是很明显的。

#include<cstdio>
#include<vector>
#include<iostream>
#define lg(x) (31-__builtin_clz(x))
using std::vector;
const int N=524290,M=1000086;
int n,m;
long long Max(long long a,long long b){return a>b?a:b;}
long long Min(long long a,long long b){return a<b?a:b;}
int Max(int a,int b){return a>b?a:b;}
int Min(int a,int b){return a<b?a:b;}
struct infor{//信息
	long long l,r,s,m;infor(){l=s=r=m=0;}
	infor(long long _l,long long _r,long long _s,long long _m){l=_l;r=_r;s=_s;m=_m;}
	infor operator +(const infor b){
		return infor(Max(l,s+b.l),Max(b.r,r+b.s),s+b.s,Max(Max(m,b.m),r+b.l));
	}
};
infor tmp[N];
struct Neko{//猫树
	int sze,lsz;
	vector<int>lp,rp,md;
	vector<infor>p;
	bool build(int k,int l,int r,infor *b){
		lp[k]=p.size();rp[k]=lp[k]+(r-l+1)-1;
		if(l==r){p.push_back(b[l]);return true;}
		int mid=(l+r)>>1,len=0;tmp[0]=infor();md[k]=mid;
		for(int i=mid;i>=l;i--){tmp[len+1]=b[i]+tmp[len];len++;}
		for(int i=len;i;i--)p.push_back(tmp[i]);
		p.push_back(b[mid+1]);
		for(int i=mid+2;i<=r;i++){
			infor lst=p.back();
			p.push_back(lst+(i>sze?infor():b[i]));
		}
		build(k<<1,l,mid,b);build(k<<1|1,mid+1,r,b);
		return true;
	}
	bool init(int n,infor *b){
		sze=n;lsz=lg(n);if((1<<lsz)!=n)lsz++;
		lp.resize(2<<lsz);rp.resize(2<<lsz);md.resize(2<<lsz);
		return build(1,1,1<<lsz,b);
	}
	infor ask(int l,int r){
		int pl=(l-1)|(1<<lsz),pr=(r-1)|(1<<lsz);
		if(l==r)return p[lp[pl]];int lca=pl>>(lg(pl^pr)+1);
		int mp=(lp[lca]+rp[lca])>>1;return p[mp-md[lca]+l]+p[mp+r-md[lca]];
	}
};
struct dnn{//窦树(迫真主席树)
	Neko t[M];
	infor a[M];
	int B[M],sze,pc;
	vector<int>s[M];
	bool build(int k,int l,int r,infor *b){
		B[k]=lg(r-l+1);int lpc=pc;
		for(int i=l;i<=r;i+=B[k])s[k].push_back(++pc);
		for(int i=l,j=0;i<=r;i+=B[k],j++){
			if(Min(B[k],r-i+1)!=1)build(s[k][j],i,Min(i+B[k]-1,r),b);
			else a[s[k][j]]=b[i];
			a[k]=a[k]+a[s[k][j]];
		}
		t[k].init(s[k].size(),a+lpc);return true;
	}
	bool init(int x,infor *b){sze=x;return build(pc=1,1,x,b);}
	#define gb(p,b) ((p-1)/B[k]+1)
	infor ask(int k,int l,int r,int x,int y){
		if(l==x&&r==y)return a[k];int lp=gb(x-l+1,B[k]),rp=gb(y-l+1,B[k]);
		if(lp==rp)return ask(s[k][lp-1],l+B[k]*(lp-1),Min(r,l+B[k]*lp-1),x,y);
		else if(lp+1==rp){
			infor r1=ask(s[k][lp-1],l+B[k]*(lp-1),l+B[k]*lp-1,x,l+B[k]*lp-1);
			infor r2=ask(s[k][rp-1],l+B[k]*lp,Min(l+B[k]*rp-1,r),l+B[k]*lp,y);
			return r1+r2;
		}
		else{
			infor r1=ask(s[k][lp-1],l+B[k]*(lp-1),l+B[k]*lp-1,x,l+B[k]*lp-1);
			infor r2=ask(s[k][rp-1],l+B[k]*(rp-1),Min(l+B[k]*rp-1,r),l+B[k]*(rp-1),y);
			return r1+t[k].ask(lp+1,rp-1)+r2;
		}
	}
};
infor a[N];dnn d;
int read(){
	char ch=getchar();int nn=0,ssss=1;
	while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
	return nn*ssss;
}
int main(){
	freopen("dou.in","r",stdin);
	freopen("dou.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++){
		int x=read();
		a[i]=infor(Max(x,0),Max(x,0),x,Max(x,0));
	}
	d.init(n,a);
	for(int i=1;i<=m;i++){
		int l=read();int r=read();
		std::cout<<d.ask(1,1,n,l,r).m<<'\n';
	}
}
posted @ 2022-04-22 21:07  LFCode  阅读(159)  评论(2编辑  收藏  举报