[HNOI2002]营业额统计

花了一天钻研了splay,然后发现splay没我想象的那么难……以前都是写SBT来着……  但是splay的速度确实没那么快,但是真的挺好写的~

我写的版本测了这题以后又用了一下别人的splay,发现通过这题大多数splay在750ms上下,我是688ms,说明还算是不错的啦~啦啦啦~


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;

struct node
{
	int count, key;
	node *left_son, *right_son, *father;
	node():count(0), key(0), left_son(NULL), right_son(NULL), father(NULL){}
	node(int tmp, node *tmp_node):count(1), key(tmp), left_son(NULL), right_son(NULL), father(tmp_node){}
}*proot = new node;

//返回a指针的儿子数量
#define RNC(a) ((a)==NULL?0:a->count)

void pg(node &t)
{
	cout<<"key = "<<t.key<<" count = "<< t.count <<" " << endl;
	if (t.father != NULL)	cout <<"fa ="<<t.father -> key<<" ";
	else cout<<"no father ";
	if (t.left_son != NULL)	cout<<"L = "<<t.left_son -> key<<" ";
	else cout<<"no L ";
	if (t.right_son != NULL)	cout<<"R = "<<t.right_son -> key<<" ";
	else cout<<"no R ";
	cout<<endl<<endl;
	if (t.left_son != NULL)		pg(*t.left_son);
	if (t.right_son != NULL)	pg(*t.right_son);
}

void zig(node &x) //右旋
{

	node *y =x.father; 
	x.count = RNC(x.left_son) + RNC(x.right_son) + 1 + RNC(y -> right_son) + 1;
	y -> count = x.count - 1 - RNC(x.left_son);
	/*重新出x节点,和y节点的儿子数量*/
	y -> left_son = x.right_son;
	if (x.right_son != NULL)	x.right_son -> father = y;
	x.father = y -> father;
	if (y -> father != NULL)
		if (y -> father -> left_son == y)	y -> father -> left_son = &x;
		else y -> father -> right_son = &x;
	y -> father = &x;
	x.right_son = y;
}

void zag(node &x)
{
	node *y = x.father;
	x.count = RNC(x.right_son) + RNC(x.left_son) + 1 + RNC(y -> left_son) + 1;
	y -> count = x.count - 1 - RNC(x.right_son);
	y -> right_son = x.left_son;
	if (x.left_son != NULL)		x.left_son -> father = y;
	x.father = y -> father;
	if (y -> father != NULL)
		if (y -> father -> left_son == y)	y -> father -> left_son = &x;
		else y -> father -> right_son = &x;
	y -> father = &x;
	x.left_son = y;
}

void splay(node &x)
{
	node *y;
	while ((y = x.father) != NULL)
	{
		if (y -> father == NULL)
		{
			if (&x == y -> left_son)	zig(x);	
			else zag(x);
			break;
		}
		if (&x == y -> left_son)
		{
			if (y == y -> father -> left_son)	
			{
				zig(*y);
				zig(x);	
			}else{
				zig(x);
				zag(x);	
			}
		}else
		{
			if (y == y -> father -> right_son)	
			{
				zag(*y);

				zag(x);	
			}else{
				zag(x);
				zig(x);	
			}
		}
	}
	proot = &x;
}

node* search_node(int valve, node &t = *proot)//搜索valve从t节点开始搜索,找不到,就返回最后找到的位置
{
	node *x = &t;
	while (x -> key != valve)
	{
		if (valve <= x -> key)	
		{
			if (x -> left_son == NULL)	break;
			x = x -> left_son;	
		}else{
			if (x -> right_son == NULL)	break;	
			x = x -> right_son;
		}
	}
	return x;
}


void insert_node(int valve)
{
	if (!proot -> count)
	{
		proot -> count = 1;	
		proot -> key = valve;
		return;
	}
	node *pos(search_node(valve, *proot));
	if (valve <= pos -> key)
	{
		pos -> left_son = new node(valve, pos);	
		splay(*(pos -> left_son));
	}else 
		{
			pos -> right_son = new node(valve, pos);
			splay(*(pos -> right_son));
		}
}

/*
 * 寻找从x节点为根的极大值
 * 直接去search_node去找一个极大值,一定找不到,最后找到的那个点,就是最大值
 * 把最大值调整到他们通过父亲节点所能追溯到的最早的节点
 * PS:为何不直接调整到根?为何总是要调整? 其实都是代码省事的 > 真正的效率……
*/
node* exterme_max(node &x) //找从x节点为根的最大值
{
	node *pos = search_node(0x7fffffff, x);
	splay(*pos);
	return pos;
}

node* exterme_min(node &x)
{
	node *pos = search_node(-0x7fffffff, x);
	splay(*pos);
	return pos;
}

/*
 * 从整棵树中删除值为valve的节点
 * 找到valve,旋转到根
 * 如果只有一个节点,那么要特殊判断一下
 * 如果根没有左儿子,那么直接让右儿子当根
 * 如果有左儿子,那么找到左儿子的最大值,旋转到左儿子的位置上
 * 此时,[根的左儿子]一定没有[右儿子](他最大,当然没有右儿子)
 * 把[根的右儿子],并到[根的左儿子]的右儿子上
*/
bool delete_node(int valve)
{
	node *pos = search_node(valve, *proot);
	if (pos -> key != valve)	return false;//根本没有valve 删除失败
	splay(*pos);
	if (proot -> left_son == NULL)
	{
		proot = pos -> right_son;
		delete pos;
	}else{
		proot -> left_son -> father = NULL;	
		proot = exterme_max(*(pos -> left_son));
		delete pos;
	}
}

/*找前驱
 * 先找到valve,把valve旋转到根节点
 * 这个时候,根左儿子的最大值,和右儿子最小值,就是valve的前驱。  后继同理
 * PS:返回下标,有利于判断是否不存在前驱后继
*/

node* find_node_pre(int valve)
{
	node *pos = search_node(valve, *proot);
	splay(*pos);
	if (proot -> left_son == NULL)	return NULL;
	return exterme_max(*(proot -> left_son));
}
//找后继 
node* find_node_next(int valve)
{
	node *pos = search_node(valve, *proot);
	splay(*pos);
	if (proot -> right_son == NULL)	return NULL;
	return exterme_min(*(proot -> right_son));
}

//查询valve的排名1       ****因为本题没有设计查排名操作,所以查排名操作没有验证对错
/*
 * 查询valve排名,和其他二叉树方法一样
*/

int rank_node(int valve)
{
	int ans = 0;
	node *x = proot;
	while (x -> key != valve)
	{
		if (valve <= x ->key)	
		{
			if (x -> left_son == NULL)	return -1;
			x = x -> left_son;	
		}else
		{
			if (x -> right_son == NULL)	return -1;	
			ans += 1 + RNC(x -> left_son);
			x = x-> right_son;	
		}
	}
	ans += RNC(x -> left_son);
	return ans;

}

//查排名操作2
int find_rank_node(int valve)
{
	search_node(valve, *proot);
	return RNC(proot -> left_son) + 1;
}
int n, tmp, ans;

int main()
{
	ios::sync_with_stdio(false);
	cin >> n;
	cin >> tmp;
	--n;
	ans = tmp;
	insert_node(tmp);
	while (n--)
	{
		if ( (cin >> tmp) == NULL)	tmp =0;
		if (search_node(tmp) -> key == tmp)	continue;
		insert_node(tmp);
		node *pre_node = find_node_pre(tmp);
		node *next_node = find_node_next(tmp);
		int a, b;
		if (pre_node == NULL)	a = 0x7fffffff;
		else a = pre_node -> key;
		if (next_node == NULL)	b = 0x7fffffff;
		else b = next_node -> key;
		a = abs(a - tmp);
		b = abs(b - tmp);
		ans += min(a, b);
	}
	cout << ans << endl;
	return 0;
}

花了时间研究zkw splay tree,也就是自顶向下维护的神奇splay,各种快!而且程序更短功能更好用!!!决定用这个程序当模板了!


强烈推荐,速度快了6倍!


#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

const int maxint = 0x7fffffff;

struct node
{
	int key;
	int size;
	node *c[2];
	node(): key(0), size(0){c[0] = c[1] = this;}
	node (int KEY_, node *c0, node *c1):
	key(KEY_){c[0] = c0, c[1] = c1;}
	node* rz(){return size = c[0] -> size + c[1] -> size + 1, this;}
} Tnull, *null = &Tnull;

struct splay
{
	node *root;
	splay()
	{
		root = (new node(*null)) -> rz();
		root -> key = maxint;
	}

	void zig(bool d)
	{
		node *t = root -> c[d];	
		root -> c[d] = null -> c[d];	
		null -> c[d] = root;
		root = t;
	}

	void zigzig(bool d)
	{
		node *t = root -> c[d] -> c[d];	
		root -> c[d] -> c[d] = null -> c[d];
		null -> c[d] = root -> c[d];
		root -> c[d] = null -> c[d] -> c[!d];
		null -> c[d] -> c[!d] = root -> rz();
		root = t;
	}

	void finish(bool d)
	{
		node *t = null -> c[d], *p = root -> c[!d];	
		while (t != null)
		{
			t = null -> c[d] -> c[d];	
			null -> c[d] -> c[d] = p;
			p = null -> c[d] -> rz();
			null -> c[d] = t;	
		}
		root -> c[!d] = p;
	}

	void select(int k)
	{
		int t;
		while (1)	
		{
			bool d = k > (t = root -> c[0] -> size);		
			if (k == t || root -> c[d] == null)	break;
			if (d)	k -= t + 1;
			bool dd = k > (t = root -> c[d] -> c[0] -> size);
			if (k == t || root -> c[d] -> c[dd] == null){zig(d); break;}
			if (dd) k -= t + 1;
			d != dd ? zig(d), zig(dd) : zigzig(d);
		}
		finish(0), finish(1);
		root -> rz();
	}

	void search(int x)
	{
		while (1)		
		{
			bool d = x > root -> key;
			if (root -> c[d] == null)	break;	
			bool dd = x > root -> c[d] -> key;
			if (root -> c[d] -> c[dd] == null){zig(d);break;}
			d != dd ? zig(d), zig(dd) : zigzig(d);
		}	
		finish(0); finish(1);
		root -> rz();
		if (x > root -> key)	select(root -> c[0] -> size + 1);
	}


	void ins(int x)
	{
		search(x);
		node *oldroot = root;	
		root = new node(x, oldroot -> c[0], oldroot);
		oldroot -> c[0] = null;
		oldroot -> rz();
		root -> rz();
	}

	void del(int x)
	{
		search(x);	
		node *oldroot = root;
		root = root -> c[1];
		select(0);
		root -> c[0] = oldroot -> c[0];
		root -> rz();
		delete oldroot;
	}
	int sel(int k){return select(k - 1), root -> key;}
	int ran(int x){return search(x), root -> c[0] -> size + 1;}
}sp;


int n, tmp, ans;

int main()
{


	ios::sync_with_stdio(false);
	cin >> n;
	cin >> tmp;
	--n;
	ans = tmp;
	sp.ins(tmp);
	while (n--)
	{
		if ( (cin >> tmp) == NULL)	tmp =0;
		sp.ins(tmp);
		int rank = sp.ran(tmp);
		int a = maxint, b = maxint;
		int t1 = maxint/2, t2 = maxint;
		if (rank != 1)
		{
			t1 = a = sp.sel(rank - 1);
			a = abs(a - tmp);	
		}
		t2 = b = sp.sel(rank + 1);
//		cout<<rank<<" "<<t1<<" "<<t2<<endl;
		b = abs(b - tmp);
		ans += min(a, b);
	}
	cout << ans << endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值