浅谈动态开点线段树

兴致勃勃的弱鸡想去学主席树了!

可惜突然发现动态开点线段树不会了。。。。

发挥大胆猜想,不用求证的精神:你们都会线段树

众所周知:线段树空间复杂度是O4*n,这就不用证,因为我菜

那当n很大,操作正常时,如果像正常的线段树一样(记左儿子为n+n,右儿子为n+n+1),再开四倍n会炸内存

怎么办呢?

想一想:时间复杂度与原来几乎差不多 mlg n,与原来差距只有没几倍的常数而已

但是为什么每次操作时间才lg n?

因为每次只会经过·lg n个结点,也就是总共最多经过m lg n个结点

再m lg n远远小于4n的情况下,再开4n的结点不是很浪费吗?

不一定每个点都有用

所以就可以动态开点啦!

记左儿子lc,右儿子rc就够了

例题:Luogu P1908 逆序对

https://www.luogu.org/problemnew/show/P1908

就是求个逆序对

可以离散化后树状数组,线段树

但是可以用动态开点线段树!

按上文的说法,nlgn的空间,开3个数组,必炸内存,但是我只开了1e7的数组还是过了,

哈哈哈哈

#include<cstdio>
#define ll long long
using namespace std;

const int N=1e7+7; //就这么点
int n,r;
ll ans;

struct A
{
	int cnt,c[N],lc[N],rc[N];
	
	int sum(int p,int l,int r,int x,int y)
	{
		if(!p) return 0;
		if(l==x&&r==y) return c[p];
		int mid=l+r>>1;
		if(y<=mid) return sum(lc[p],l,mid,x,y);
		if(x>mid) return sum(rc[p],mid+1,r,x,y);
		return sum(lc[p],l,mid,x,mid)+sum(rc[p],mid+1,r,mid+1,y);    
	}
	
	void add(int &p,int l,int r,int x,int k)
	{
		if(!p) p=++cnt; 
		if(l==r) 
		{
			c[p]+=k; return;
		}
		int mid=l+r>>1;
		if(x<=mid) add(lc[p],l,mid,x,k);
			else add(rc[p],mid+1,r,x,k);
		c[p]=c[lc[p]]+c[rc[p]];
	}
}tree;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x; scanf("%d",&x);
		ans+=tree.sum(r,1,1e9,x+1,1e9);
		tree.add(r,1,1e9,x,1);
	}
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/YYHS_WSF/article/details/84105107