hdu 4441 splay tree 2012天津 K题

·http://acm.hdu.edu.cn/showproblem.php?pid=4441

代码写了两个小时,可是AC却在两天后

最近犯的错误都是肉眼无法辨别啊,下次写这种数据结构题一定仔细再仔细

网上应该已经有人写过题解了,我的做法大体相同,稍有不同

1:找不在集合中的最小的正数只需维护一个set即可。set保存的是所有不在集合中的数。删除一对+i -i 的时候就在set插入一个+i,插入i的时候就在set中删掉

2:怎么插入-i,很显然,找到i后面第一个正数对应的负数即可,-i就插在这个负数的前面,因为最迟不能迟于这个负数出队列

所有的操作对于伸展树来说都是模拟而已,只要敲好那三个函数,调试的时候输出整棵树看一下就一目了然

我依旧水水的~

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <map>
#include <set>
typedef __int64 lld;
const int inf = ~0u >> 2;
using namespace std;
const int maxn = 300010;
//splay tree
#define L ch[x][0]
#define R ch[x][1]
#define KT (ch[ch[rt][1]][0])
struct SplayTree {
	int ch[maxn][2];
	int pre[maxn], sz[maxn], val[maxn];
	int rt, top;
	int cnt[maxn];
	void Rotate(int x, int f) {
		int y = pre[x];
		ch[y][!f] = ch[x][f];
		pre[ch[x][f]] = y;
		pre[x] = pre[y];
		if (pre[x])
			ch[pre[y]][ch[pre[y]][1] == y] = x;
		ch[x][f] = y;
		pre[y] = x;
		up(y);
	}
	void Splay(int x, int goal) {
		while (pre[x] != goal) {
			if (pre[pre[x]] == goal)
				Rotate(x, ch[pre[x]][0] == x);
			else {
				int y = pre[x], z = pre[y];
				int f = (ch[z][0] == y);
				if (ch[y][f] == x)
					Rotate(x, !f), Rotate(x, f);
				else
					Rotate(y, f), Rotate(x, f);
			}
		}
		up(x);
		if (goal == 0)
			rt = x;
	}
	void RTO(int k, int goal) {
		int x = rt;
		while (sz[L] + 1 != k) {
			if (k < sz[L] + 1)
				x = L;
			else {
				k -= (sz[L] + 1);
				x = R;
			}
		}
		Splay(x, goal);
	}
	void up(int x) {
		sz[x] = 1 + sz[L] + sz[R];
		cnt[x] = cnt[L] + cnt[R] + (val[x] > 0);
		sum[x] = val[x] + sum[L] + sum[R];
	}
	void Newnode(int &x, int c, int f) {
		x = ++top;
		if (c < 0)
			neg[-c] = x;
		else
			posi[c] = x;
		L = R = 0;
		sz[x] = 1;
		pre[x] = f;
		val[x] = sum[x] = c;
		cnt[x] = (val[x] > 0);
	}
	void init(int n) {
		mm.clear();
		for (int i = 1; i <= n; i++)   mm.insert(i);
		ch[0][0] = ch[0][1] = pre[0] = 0;
		sz[0] = rt = top = 0; cnt[0]=0;
		sum[0] = 0;
		Newnode(rt, 0, 0);
		Newnode(ch[rt][1],0, rt);
		up(ch[rt][1]);
		up(rt);
	}
	void vist(int x) {
		if(x) {
			printf("结点%2d : 左儿子  %2d   右儿子  %2d  val: %2d sum=%d\n",x,ch[x][0],ch[x][1],val[x],sum[x]);
			vist(L);
			vist(R);
		}
	}
	void debug() {
		puts("");
		vist(rt);
		puts("");
	}
	void Find(int x,int &ans) {
		if(!x) return ;
		if(cnt[L]) {
			Find(L,ans);
		} else if(val[x] > 0) {
			ans=x;
			return ;
		} else {
			Find(R,ans);
		}
	}
	void Insert(int position) {
		int l = position + 1, r = position + 2;
		int v = *mm.begin();
		mm.erase(mm.begin());
		RTO(l, 0);
		RTO(r, rt);
		Newnode(KT, v, ch[rt][1]);
		up(ch[rt][1]);
		up(rt);
		Splay(posi[v],0);
		int node = -1;
		if(cnt[ch[rt][1]])    Find(ch[rt][1],node);
		if (node == -1) {
			RTO(sz[rt] - 1, 0);
			RTO(sz[rt], rt);
			Newnode(KT, -v, ch[rt][1]);
			up(ch[rt][1]);
			up(rt);
		} else {
			int VV = val[node];
			Splay(neg[VV], 0);
			RTO(sz[ch[rt][0]], 0);
			Splay(neg[VV],rt);
			Newnode(KT, - v, ch[rt][1]);
			up(ch[rt][1]);
			up(rt);
		}
	}
	void Query(int num) {
		int a=posi[num],b=neg[num];
		Splay(a, 0);
		Splay(b, rt);
		printf("%I64d\n", sum[KT]);
	}
	void Remove(int num) {
		mm.insert(num);
		Splay(posi[num], 0);
		Del_Root();
		Splay(neg[num], 0);
		Del_Root();
	}
	void Del_Root() {
		int t = rt;
		if (ch[rt][1]) {
			rt = ch[rt][1];
			RTO(1, 0);
			ch[rt][0] = ch[t][0];
			if (ch[rt][0])
				pre[ch[rt][0]] = rt;
		} else
			rt = ch[rt][0];
		pre[rt] = 0;
		up(rt);
	}
	int posi[maxn]; //某个正数对应的伸展树中的节点
	int neg[maxn];  //某个负数对应的伸展树中的节点
	lld sum[maxn];
	set<int> mm;    //保存当前不在集合中的最小的正数
} spt;
int main() {
	int n, num,ca=1;
	char cmd[20];
	while (scanf("%d", &n) != EOF) {
		printf("Case #%d:\n",ca++);
		spt.init(n);
		for (int i = 0; i < n; i++) {
			scanf("%s%d", cmd, &num);
			if (cmd[0] == 'i') {
				spt.Insert(num);
			} else if (cmd[0] == 'q') {
				spt.Query(num);
			} else {
				spt.Remove(num);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值