笛卡尔树

一些约定:不管是队列、数组,还是栈,都从1号开始 存放数据
因此队列初始化就设 head=1,tail=0,栈初始化设top=0,++top后存数

习题练习
https://www.cnblogs.com/reverymoon/p/9525764.html
https://www.cnblogs.com/HocRiser/p/10620518.html

笛卡尔树线性建立思路:
利用单调栈线性构造笛卡尔树。单调栈递增则维护最小堆,递减则维护最大堆。以维护最小堆为例。
遍历原序列的每一个节点,然后与栈顶元素比较,如果当前节点小于栈顶节点,则弹出栈顶节点,并把弹出节点设为当前节点的左子树。一直到当前节点大于栈顶节点为止,把当前节点设为栈顶节点的右子树,并把当前节点入栈。
这里都是原序列的索引操作。

int Build(int n)
{
	int top=0,stack[maxn];
	for(int i=1;i<=n;++i)
	{
		while(top&&A[stack[top]]>A[i])
			CT[i].l=stack[top--];
		if(top)
			CT[stack[top]].r=i;
		stack[++top]=i;
	}
	return stack[1];
}

笛卡尔树递归构造O(n2):
每次选取区间最大值作为根,然后往两边递归也可以建树。直接暴力是O(n2)的,线段树优化一下就可以O(nlogn)

HDU -1506

const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

int A[maxn],n;
ll ans;

struct CartesianTree
{
	int l,r;

}CT[maxn];

void Init(int n)
{
	for(int i=1;i<=n;++i)
		CT[i].l=CT[i].r=0;
}

int Build(int n)
{
	int top=0,stack[maxn];
	for(int i=1;i<=n;++i)
	{
		while(top&&A[stack[top]]>A[i])
			CT[i].l=stack[top--];
		if(top)
			CT[stack[top]].r=i;
		stack[++top]=i;
	}
	return stack[1];
}

int dfs(int root)
{
	if(root==0)
		return 0;
	ll cnt=dfs(CT[root].l)+dfs(CT[root].r)+1;
	ans=max(ans,cnt*A[root]);
	return cnt;
}

int main()
{
	while(scanf("%d",&n)&&n)
	{
		Init(n);
		rep(i,1,n)
			cin>>A[i];
				
		int root=Build(n);
		ans=0;
		dfs(root);
		cout<<ans<<"\n";
	}  
	return 0;
}

POJ - 1785

const int maxn=5e4+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

struct CartesianTree
{
	int l,r;
	char key[100];
	int value;
	bool operator<(const CartesianTree &b) const
	{
		return strcmp(key,b.key)<0;
	}
}CT[maxn];

int Build(int n)
{
	int top=0,stack[maxn];
	for(int i=1;i<=n;++i)
	{
		while(top&&CT[i].value>CT[stack[top]].value)
			CT[i].l=stack[top--];
		if(top)
			CT[stack[top]].r=i;
		stack[++top]=i;	
	}
	return stack[1];
}

void InOrder(int rt)
{
	if(rt==0)
		return;
	printf("(");
	InOrder(CT[rt].l);
	printf("%s/%d",CT[rt].key,CT[rt].value);
	InOrder(CT[rt].r);
	printf(")");
}

int main()
{
	int n;
	while(scanf("%d",&n)&&n)
	{
		rep(i,1,n)
		{
			scanf(" %[^/]/%d",&CT[i].key,&CT[i].value);	
			CT[i].l=CT[i].r=0;		
		}
		sort(CT+1,CT+1+n);
				
		int rt=Build(n);
		InOrder(rt);
		printf("\n");
	}    
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值