【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
原创粉丝点击