【Splay练习】
来源:互联网 发布:mac ds store 文件 编辑:程序博客网 时间:2024/06/04 01:17
POJ 3481 Double Queue
每个顾客有 编号1e6,优先级1e7
银行可以先服务 当前队列中优先级最大(2)或者最小(3)的顾客, 输出其编号
我们可以按 优先级 来建树的相对顺序
然后对于 优先级为x 的顾客我们 在树中寻找他是第几个 (k),然后进行操作。
const int INF=0x3f3f3f3f;#define N 200005#define key_value ch[ch[root][1]][0] //经常利用这个int pre[N],ch[N][2],key[N],size[N];int sx[N];int root,tot;int a[N];void newnode(int &r,int father,int p,int k){ //注意r 需要引用 r=++tot; //可知我们的根节点从1开始 pre[r]=father; //更新 pre size[r]=1; //新开节点只有自己 ch[r][0]=ch[r][1]=0; sx[r]=p; key[r]=k;}void push_up(int r){ //向上更新 size[r]=size[ch[r][0]] + size[ch[r][1]] +1;}void Rotate(int x,int kind)//对X旋转,0为左旋,1为右旋 该部分基本固定{ int y=pre[x]; ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; push_up(y);}void Splay(int r,int goal) ////Splay调整,将结点r调整到goal下面{ while(pre[r]!=goal) { if(pre[pre[r]]==goal) { //这题有反转操作,需要先push_down,在判断左右孩子 Rotate(r,ch[pre[r]][0]==r); } else { //这题有反转操作,需要先push_down,在判断左右孩子 int y=pre[r]; int kind=(ch[pre[y]][0]==y); //两个方向不同,则先左旋再右旋 if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } //两个方向相同,相同方向连续两次 else { Rotate(y,kind); Rotate(r,kind); } } } push_up(r); if(goal==0)root=r;}int Get_Min(int r) //找到最值{ while(ch[r][0]) { r=ch[r][0]; } return r;}int Get_Max(int r){ while(ch[r][1]) { r=ch[r][1]; } return r;}int get_kth(int r,int k) //我们得到的是第k个节点的位置。但是我们注意init的时候添加两个极大极小点。{ int t=size[ch[r][0]]+1;// 我们添加过一个很小的点。 if(t==k)return r; if(t>k)return get_kth(ch[r][0],k); else return get_kth(ch[r][1],k-t);}int get(int p,int now,int cnt){ // 优先级为p 在 树中是第几个 if(size[now]==0 ){ //题意没有相同的 p? return cnt; } if(p<sx[now]){ get(p,ch[now][0],cnt); } else{ get(p,ch[now][1],cnt+1+size[ ch[now][0] ]); }}//void insert(int x,int p,int k){ // 插入一个节点 到 第x个后面// Splay(get_kth(root,x+1),0); // x转至根// Splay(get_kth(root,x+2),root); //x+1转到root右儿子,此时x+2.... 都在右边// newnode(key_value,ch[root][1],k,p);// push_up(ch[root][1]);// push_up(root);//}void insert(int p,int k){ int pos=get(p,root,0); // 我们应该放在 pos的后面,但包含最小点,所以应该是放在pos-1的后面// printf("p= %d pos=%d\n",p,pos); Splay(get_kth(root,pos),0); // pos-1 Splay(get_kth(root,pos+1),root); // pos newnode(key_value,ch[root][1],p,k); push_up(ch[root][1]); push_up(root);// printf("now now+1 %d %d\n",key[root],key[ch[root][1]]);}void del(int x){ // 删除第x个 Splay(get_kth(root,x),0); // x-1 移到0 Splay(get_kth(root,x+2),root); //x+1 移到x-1的右边 那么key_value=x printf("%d\n",key[key_value] ); pre[key_value]=0; key_value=0; //不知道算不算清除 // push_up(ch[root][1]); push_up(root);}void init(){ tot=root=0; ch[root][0]=ch[root][1]=pre[root]=size[root]=0; //root 地址为0,所以不需要赋m[root]的值 /*开一个理论上永远最大的根,和永远最小的根,这样操作有套路*/ newnode(root,0,-INF,0); // 这个点是永远最小的 newnode(ch[root][1],root,INF,0); //这个点永远最大// sx[ch[root][1]]=INF; push_up(ch[root][1]); //记得建完树之后更新到root push_up(root);}int main(){ // freopen("1.txt","r",stdin); init(); int op=1; int p,k; while(1){ scanf("%d",&op); if(op==1){ scanf("%d %d",&k,&p); insert(p,k);// printf("1111111 :%d\n",size[root]); } else if(op==2){ //最高 一共有size[root]-2个 if(size[root]==2){ printf("0\n"); continue; } del(size[root]-2); } else if(op==3){ if(size[root]==2){ printf("0\n"); continue; } del(1); } else break; } return 0;}
POJ 2828 Buy Tickets
Splay 竟然可耻的 TLE 了
我们用线段树可以比较容易的解决此问题, 解法有点类似逆序数。
HDU 1754 I Hate It
也是线段树啊。。。。别搞事啊。
HDU 3468 Treasure Hunting
这次更奇葩,这是个二分图匹配问题啊。。。。
HDU 1890 Robotic Sort
这个题很经典
using namespace std;#define Key_value ch[ch[root][1]][0]#define ll __int64const int N=100005;const int INF=0x3f3f3f3f;int pre[N],key[N],ch[N][2],root,tot1;//分别表示父结点,键值,左右孩子(0左1右),根结点,结点数量int n;ll sum[N]; // 字数的和int size[N]; //子树节点数int a[N];int rev[N]; //lazystruct node{ int v; int i;}f[N];//新建一个结点void NewNode(int &r,int father,int k) //新建节点 &的意义就是将编号(tot1)直接传给参数{ r=k; // 二叉排序树在插入节点的时候就已经建好了,这个r这是一个节点编号 pre[r]=father; size[r]=1; // size包括的自己 rev[r]=0; ch[r][0]=ch[r][1]=0;//左右孩子为空}void push_rev(int r){ if(!r) return; swap(ch[r][0],ch[r][1]); rev[r]^=1; //}void push_up(int r){ //更新 r的sum和size size[r]=size[ch[r][0]] + size[ch[r][1]]+1;}void push_down(int r){ if(rev[r]){ push_rev(ch[r][0]); //直接交换,不再判断了 push_rev(ch[r][1]); rev[r]=0; }}//建树//先建立中间结点,再两端的方法void build(int &x,int l,int r,int father){ //这种建树方式 = 按位置大小 来建一颗二叉排序树 if(l>r) return; int mid=(l+r)>>1; NewNode(x,father,mid); //可以看出,就是拿原序列建树,并且树中节点编号就是原序列位置 build(ch[x][0],l,mid-1,x); build(ch[x][1],mid+1,r,x); push_up(x); //更新一下}void init(){ root=tot1=0; ch[root][0]=ch[root][1]=pre[root]=size[root]=rev[root]=0; NewNode(root,0,n+1); //先新建的头结点 NewNode(ch[root][1],root,n+2); //在头结点的右边再插入一个新节点,这个节点的位置十分精髓 //二叉排序树,只要插进去了,就永远决定的他们的大小关系,与key值就没有关系了,仔细想想不难发现 //这个节点,是所有节点最大的一个位置,因为整个序列都处在他的左子树中。 build(Key_value,1,n,ch[root][1]); // 从第二个节点开始建树 //记得维护下两个头结点 push_up(ch[root][1]); push_up(root);}/* 1 2 3 4 5 0 | 3 / \ 1 5 / \ / \空 2 4 6 *///旋转,kind为1是右旋,为0是左旋void Rotate(int x,int kind) //x 表示要旋转的节点编号{ int y=pre[x]; push_down(y); push_down(x); ch[y][!kind]=ch[x][kind]; //父节点相反关系的子节点 = 节点的相同关系节点 pre[ch[x][kind]]=y; // 更新pre数组 if(pre[y]) // 如果Y存在(rotate到的位置不是根的话),要把y的儿子=x。 ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; push_up(y);}//Splay调整,将结点r调整到goal下面void Splay(int r,int goal){ push_down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal){ //如果有反转操作,需要先push_down push_down(pre[r]); push_down(r); Rotate(r,ch[pre[r]][0]==r); } else { push_down(pre[pre[r]]); push_down(pre[r]); push_down(r); int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) //如果三点一线,则需要先 root 父亲结点 { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } push_up(r); //更新根结点 if(goal==0) root=r;}int get_kth(int r,int k){ //根据上面的建树方式可知是找序列中的第k个数(的编号) push_down(r); int t=size[ch[r][0]]+1; if(t==k) return r; if(t>k) return get_kth(ch[r][0],k); else return get_kth(ch[r][1],k-t);}//找后继的节点编号int get_next(int x){ push_down(x); int tmp=ch[x][1]; //找右子树中最小的节点 if(0==tmp) return -1; while(ch[tmp][0]){ //如果有反转 ,这里也要push 不然Tle tmp=ch[tmp][0]; push_down(tmp); } return tmp;}bool cmp(node a,node b){ if(a.v==b.v) return a.i<b.i; return a.v<b.v;}int main(){ //freopen("1.txt","r",stdin); while(~scanf("%d",&n) && n){ for(int i=1;i<=n;i++){ scanf("%d",&f[i].v); f[i].i=i; } sort(f+1,f+n+1,cmp); init(); for(int i=1;i<=n;i++){ //先输出第i大的数在什么地方 // f[i].v 即第i大的数字,在树中的位置f[i].i; Splay(f[i].i,0); //用get_kth返回的是 树中的节点值 if(i==1) printf("%d",size[ch[root][0]]); //因为有一个初识节点在里面,所以不用+1 else printf(" %d",size[ch[root][0]]); //然后反转区间[i,node[i].i] Splay(get_kth(root,i),0); //先转 i-1 int k=get_next(f[i].i); Splay(k,root); push_rev(Key_value); } printf("\n"); } return 0;}
HDU 3436 Queue-jumpers
HDU 3487 Play with Chain
HYSBZ 1588 营业额统计
HYSBZ 1208 宠物收养所
HYSBZ 1267 Kth Number I
HYSBZ 1269 文本编辑器editor
HYSBZ 1500 维修数列
POJ 3580 SuperMemo
HDU 2475 Box
HDU 3726 Graph and Queries
POJ 3468 A Simple Problem with Integers
0 0
- 【Splay练习】
- splay练习
- 小练习,splay区间反转
- POJ 3667 splay区间合并练习
- CODEVS-1082-线段树练习3-splay
- 【codevs1285】【BZOJ1208】宠物收养所,splay练习
- 【codevs1286】【BZOJ1503】郁闷的出纳员,splay练习
- 【Tyvj1185】【codevs1296】【BZOJ1588】营业额统计,Splay练习
- SPLAY
- splay
- splay
- splay
- Splay
- Splay
- splay
- splay
- splay
- splay
- CentOS升级Python2.6到Python2.7并安装pip
- LintCode 61 搜索区间
- web.鼠标.跟着鼠标走的事件
- selector标签 与 Drawable State详解
- Java并发-1(一)内存可见性
- 【Splay练习】
- hdu 1004 Let the Balloon Rise
- Java IO流之缓冲流
- 对ACProtect1.32壳的分析以及对抽取代码的修补
- Struts2的优势及其工作原理
- Javascript引用类型之Array类型(一)
- Mastering Opencv ch4:SFM详解(二)
- 绪论
- 页面程序获取浏览器url的方法