• 猫树总结


    猫树

    这个时候就要( exttt{orz immortalCO})了。

    问题描述

    现在你有一些无修改的信息,然后有多组询问,每一次对区间进行询问,信息满足结合律和可交换性。

    解法

    我们定义一种算法的复杂度为(O(A)+O(B)+O(C)),分别表示预处理复杂度,单次询问复杂度和空间复杂度。

    可减信息

    直接数组维护。(O(n)+O(1)+O(n))

    区间最值

    倍增的(RMQ)解决即可。(O(nlogn)+O(1)+O(nlogn))

    只支持结合律和可交换性

    一般采用线段树。(O(n)+O(logn)+O(n))

    一种新的解法:猫树

    我们仔细思考一下,对于一个点(o)而言,如果他需要递归两个儿子等同于(Lle l le mid le r le R)

    因为信息满足结合律和可交换律,所以我们只需要对于两边快速合并即可。

    然后对于每一层我们可以快速求出点(i)对于(mid)((i<mid))以及((i ge mid))的答案。

    此时唯一需要的就是快速定位点(o),如果按照一般的线段树建树显然不好定位,但是如果我们建一棵(2^{k})的树,就很容易定位了。

    具体来说,同时遍历两个点的意义就是这两个点的(Lca)对吧,相当于是你只要求出向上位移是多少即可。

    我们不妨把两两个点看成1,每4个点看成2,那么相当于就是这一段它们的异或值的(Log2)对吧。

    所以定位的深度为(Log2[pos[l]]-Log2[[pos[l] xor pos[r]])

    这时我们便找到了一个新的算法:猫树,(O(nlogn)+O(1)+O(nlogn))

    看上去很棒!

    当然猫树其实只需要保证询问是(O(1))的,只要预处理不超时都还行,所以比较适合那些询问比较多的题目。

    GSS1

    大致像线段树一样维护(lmax,rmax,lsum,rsum)然后合并即可。

    附上曾经的题解

    /*
      mail: mleautomaton@foxmail.com
      author: MLEAutoMaton
      This Code is made by MLEAutoMaton
    */
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define re register
    #define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    inline int gi()
    {
    	int f=1,sum=0;char ch=getchar();
    	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    	return f*sum;
    }
    const int N=200010,Lg=20;
    int a[N],n,pos[N],Log[N<<2];
    int p[Lg][N],s[Lg][N];
    void build(int o,int l,int r,int dep)
    {
    	if(l==r)
    	{
    		pos[l]=o;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(o<<1,l,mid,dep+1);build(o<<1|1,mid+1,r,dep+1);
    	p[dep][mid]=s[dep][mid]=a[mid];int sum=a[mid],mx=max(0,a[mid]);
    	for(int i=mid-1;i>=l;i--)
    	{
    		mx+=a[i];sum+=a[i];
    		p[dep][i]=max(p[dep][i+1],mx);
    		s[dep][i]=max(s[dep][i+1],sum);
    		if(mx<0)mx=0;
    	}
    	p[dep][mid+1]=s[dep][mid+1]=a[mid+1];sum=a[mid+1],mx=max(0,a[mid+1]);
    	for(int i=mid+2;i<=r;i++)
    	{
    		mx+=a[i];sum+=a[i];
    		p[dep][i]=max(p[dep][i-1],mx);
    		s[dep][i]=max(s[dep][i-1],sum);
    		if(mx<0)mx=0;
    	}
    }
    int query(int l,int r)
    {
    	if(l==r)return a[l];
    	int dep=Log[pos[l]]-Log[pos[l]^pos[r]];
    	return max(s[dep][l]+s[dep][r],max(p[dep][l],p[dep][r]));
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("in.in","r",stdin);
    #endif
    	n=gi();
    	for(int i=1;i<=n;i++)a[i]=gi();
    	int limit=1;
    	while(limit<n)limit<<=1;
    	build(1,1,limit,1);int T=gi();
    	for(int i=2;i<=limit<<1;i++)Log[i]=Log[i>>1]+1;
    	while(T--)
    	{
    		int l=gi(),r=gi();
    		printf("%d
    ",query(l,r));
    	}
    	return 0;
    }
    

    [Hnoi2016]序列

    如果是原题的数据范围大可以莫队做法,但是现在我们限制询问有(1e6),即(Q le 10^6)

    即现在我们讨论的这题是这个

    直接猫树维护很多东西即可。

    /*
      mail: mleautomaton@foxmail.com
      author: MLEAutoMaton
      This Code is made by MLEAutoMaton
    */
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define re register
    #define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    inline int gi()
    {
    	int f=1,sum=0;char ch=getchar();
    	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    	return f*sum;
    }
    const int N=500010,Lg=22,Inf=1e9+10,Mod=1e9+7;
    int n,a[N],sta[N],Log[N<<1];
    int pos[N],b[N],id[Lg][N],Mid[Lg][N];ll p[Lg][N],s[Lg][N],ans[Lg][N];
    int A, B, C, P;
    long long lastAns;
    
    inline int rnd() {
        return A = (A * B + (C ^ (int)(lastAns & 0x7fffffffLL)) % P) % P;
    }
    void build(int o,int l,int r,int dep)
    {
    	if(l==r)
    	{
    		pos[l]=o;return;
    	}
    	int mid=(l+r)>>1;build(o<<1,l,mid,dep+1);build(o<<1|1,mid+1,r,dep+1);
    	int top,mn;ll delta,sum,pre;
    	sta[top=0]=mid+1;delta=sum=pre=0;mn=Inf;
    	for(int i=mid;i>=l;i--)
    	{
    		int v=a[i];Mid[dep][i]=mid;
    		while(top && v<=a[sta[top]])
    		{
    			delta-=1ll*(sta[top-1]-sta[top])*a[sta[top]];
    			top--;
    		}
    		delta+=1ll*v*(sta[top]-i);
    		sta[++top]=i;mn=min(mn,v);b[i]=mn;
    		sum+=delta;pre+=mn;
    		s[dep][i]=sum;p[dep][i]=pre;
    	}
    	sta[top=0]=mid;delta=sum=pre=0;mn=Inf;
    	for(int i=mid+1;i<=r;i++)
    	{
    		int v=a[i];Mid[dep][i]=mid;
    		while(top && v<=a[sta[top]])
    		{
    			delta-=1ll*(sta[top]-sta[top-1])*a[sta[top]];
    			top--;
    		}
    		delta+=1ll*v*(i-sta[top]);
    		sta[++top]=i;mn=min(mn,v);
    		sum+=delta;pre+=mn;b[i]=mn;
    		s[dep][i]=sum;p[dep][i]=pre;
    	}
    	int pl=mid,pr=mid+1,in=0;sum=0;
    	while(l<=pl || pr<=r)
    	{
    		if(pr>r || (l<=pl && b[pl]>b[pr]))
    		{
    			id[dep][pl]=++in;int len=in-(mid-pl+1);
    			if(len)sum+=1ll*len*b[pl];
    			ans[dep][pl--]=sum;
    		}
    		else
    		{
    			id[dep][pr]=++in;int len=in-(pr-mid);
    			if(len)sum+=1ll*len*b[pr];
    			ans[dep][pr++]=sum;
    		}
    	}
    }
    ll query(int l,int r)
    {
    	if(l==r)return a[l];
    	int dep=Log[pos[l]]-Log[pos[l]^pos[r]],inl=id[dep][l],inr=id[dep][r],mid=Mid[dep][l];
    	ll Ans=s[dep][l]+s[dep][r];
    	if(inl<inr)Ans+=ans[dep][l]+1ll*(mid-l+1)*(p[dep][r]-((inl==mid-l+1)?0:p[dep][l+inl-1]));
    	else Ans+=ans[dep][r]+1ll*(r-mid)*(p[dep][l]-((inr==r-mid)?0:p[dep][r-inr+1]));
    	return Ans;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("in.in","r",stdin);
    #endif
    	n=gi();int limit=1;
    	while(limit<n)limit<<=1;
    	for(int i=2;i<=limit<<1;i++)Log[i]=Log[i>>1]+1;
    	int Q=gi();for(int i=1;i<=n;i++)a[i]=gi();
    	build(1,1,limit,1);
    	A=gi();B=gi();C=gi();P=gi();ll Ans=0;
    	while(Q--)
    	{
    		int l = rnd() % n + 1, r = rnd() % n + 1;
    		if (l > r) std::swap(l, r);
    		lastAns=query(l,r);Ans=(Ans+lastAns)%Mod;
    	}
    	printf("%lld
    ",(Ans+Mod)%Mod);
    	return 0;
    }
    
  • 相关阅读:
    分配一维动态数组or 二维动态数组的方法以及学习 new 方法or vector
    关于i++与++i的学习讨论!
    vector 中需要注意的东西!
    c++中 函数的默认参数 学习
    为什么 c++中函数模板和类模板的 声明与定义需要放到一起?
    c++中赋值运算符重载为什么要用引用做返回值?
    为什么const对象只能调用const成员函数,而不能调用非const成员函数?
    java 文件读写
    java Vector
    getRequestDispatcher
  • 原文地址:https://www.cnblogs.com/mleautomaton/p/11427003.html
Copyright © 2020-2023  润新知