三大平衡树(Treap + Splay + SBT)总结+模板

Treap树

  核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn)

Treap模板:

#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <utility>
#include <vector>
#include <queue>
#include <map>
#include <set>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 0x3f3f3f3f
#define MAXN 100005

using namespace std;

int cnt=1,rt=0; //节点编号从1开始

struct Tree
{
    int key, size, pri, son[2]; //保证父亲的pri大于儿子的pri
    void set(int x, int y, int z)
    {
        key=x;
        pri=y;
        size=z;
        son[0]=son[1]=0;
    }
}T[MAXN];

void rotate(int p, int &x)
{
    int y=T[x].son[!p];
    T[x].size=T[x].size-T[y].size+T[T[y].son[p]].size;
    T[x].son[!p]=T[y].son[p];
    T[y].size=T[y].size-T[T[y].son[p]].size+T[x].size;
    T[y].son[p]=x;
    x=y;
}

void ins(int key, int &x)
{
    if(x == 0)
        T[x = cnt++].set(key, rand(), 1);
    else
    {
        T[x].size++;
        int p=key < T[x].key;
        ins(key, T[x].son[!p]);
        if(T[x].pri < T[T[x].son[!p]].pri)
            rotate(p, x);
    }
}

void del(int key, int &x) //删除值为key的节点
{
    if(T[x].key == key)
    {
        if(T[x].son[0] && T[x].son[1])
        {
            int p=T[T[x].son[0]].pri > T[T[x].son[1]].pri;
            rotate(p, x);
            del(key, T[x].son[p]);
        }
        else
        {
            if(!T[x].son[0])
                x=T[x].son[1];
            else
                x=T[x].son[0];
        }
    }
    else
    {
        T[x].size--;
        int p=T[x].key > key;
        del(key, T[x].son[!p]);
    }
}

int find(int p, int &x) //找出第p小的节点的编号
{
    if(p == T[T[x].son[0]].size+1)
        return x;
    if(p > T[T[x].son[0]].size+1)
        find(p-T[T[x].son[0]].size-1, T[x].son[1]);
    else
        find(p, T[x].son[0]);
}

int find_NoLarger(int key, int &x) //找出值小于等于key的节点个数
{
    if(x == 0)
        return 0;
    if(T[x].key <= key)
        return T[T[x].son[0]].size+1+find_NoLarger(key, T[x].son[1]);
    else
        return find_NoLarger(key, T[x].son[0]);
}

相关题解:

POJ 3481 treap

POJ 1442 treap

POJ 2352 treap

Splay Tree(伸展树)

  核心就是 过程Splay(x, y),即将x节点转移到y节点的子节点上面(其中y是x的祖先)。

  利用其中双旋的优势能够保证查询复杂度均摊为O(lgn)

  一开始理解有些困难,其实实际上不做深入的理解就是,双旋的过程就是一个建立相对平衡的二叉树的一个过程。

  》对于二叉树,最极端的情况就是线性插入,使得整棵二叉树退化为一条链。比如你查询链的最后一个节点,之后再次查询第一个节点。

    1)若只是单旋通过Splay(x, 0)将最后一个节点移动到根节点,需要O(n)复杂度,而查询第一个节点时又需要O(n)复杂度,来来往往就退化成一条链了。

    2)若是双旋Splay(x, 0)将最后一个节点移动到根节点上时,移动过程中建立起了相对平衡的二叉树,需要O(n),也就是查询第一个节点时,大概是需要O(lgn)复杂度。这就降低了复杂度。可以证明,总的每个操作的均摊复杂度是O(lgn)。

    具体证明可以参见 杨思雨《伸展树的基本操作与应用》

I 用于维护单调队列:(以key为维护对象保证单调)

常用版:(支持相同值)

Struct Tree{

  int key, size, fa, son[2];

}

void PushUp(int x);

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To) //将x节点插入到To的子节点中

int find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处

int prev() //返回比根值小的最大值 若无返回0 若有将其转移到根处

int succ() //返回比根值大的最小值 若无返回0 若有将其转移到根处

void Insert(int key) //插入key 并且将该节点转移到根处

void Delete(int key) //删除值为key的节点 若有重点只删其中一个 x的前驱移动到根处

int GetPth(int p) //获得第p小的节点 并将其转移到根处

int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<

模板:

int cnt=1, rt=0;

struct Tree
{
    int key, size, fa, son[2];
    void set(int _key, int _size, int _fa)
    {
        key=_key;
        size=_size;
        fa=_fa;
        son[0]=son[1]=0;
    }
}T[MAXN];

inline void PushUp(int x)
{
    T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1;
}

inline void Rotate(int x, int p) //0左旋 1右旋
{
    int y=T[x].fa;
    T[y].son[!p]=T[x].son[p];
    T[T[x].son[p]].fa=y;
    T[x].fa=T[y].fa;
    if(T[x].fa)
        T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
    T[x].son[p]=y;
    T[y].fa=x;
    PushUp(y);
    PushUp(x);
}

void Splay(int x, int To) //将x节点插入到To的子节点中
{
    while(T[x].fa != To)
    {
        if(T[T[x].fa].fa == To)
            Rotate(x, T[T[x].fa].son[0] == x);
        else
        {
            int y=T[x].fa, z=T[y].fa;
            int p=(T[z].son[0] == y);
            if(T[y].son[p] == x)
                Rotate(x, !p), Rotate(x, p); //之字旋
            else
                Rotate(y, p), Rotate(x, p); //一字旋
        }
    }
    if(To == 0) rt=x;
}

int find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{
    int x=rt;
    while(x && T[x].key != key)
        x=T[x].son[key > T[x].key];
    if(x) Splay(x, 0);
    return x;
}

int prev() //返回比根值小的最大值 若无返回0 若有将其转移到根处
{
    int x=T[rt].son[0];
    if(!x) return 0;
    while(T[x].son[1])
        x=T[x].son[1];
    Splay(x, 0);
    return x;
}

int succ() //返回比根值大的最小值 若无返回0 若有将其转移到根处
{
    int x=T[rt].son[1];
    if(!x) return 0;
    while(T[x].son[0])
        x=T[x].son[0];
    Splay(x, 0);
    return x;
}

void Insert(int key) //插入key 并且将该节点转移到根处
{
    if(!rt)
        T[rt = cnt++].set(key, 1, 0);
    else
    {
        int x=rt, y=0;
        while(x)
        {
            y=x;
            x=T[x].son[key > T[x].key];
        }
        T[x = cnt++].set(key, 1, y);
        T[y].son[key > T[y].key]=x;
        Splay(x, 0);
    }
}

void Delete(int key) //删除值为key的节点 若有重点只删其中一个 x的前驱移动到根处
{
    int x=find(key);
    if(!x) return;
    int y=T[x].son[0];
    while(T[y].son[1])
        y=T[y].son[1];
    int z=T[x].son[1];
    while(T[z].son[0])
        z=T[z].son[0];
    if(!y && !z)
    {
        rt=0;
        return;
    }
    if(!y)
    {
        Splay(z, 0);
        T[z].son[0]=0;
        PushUp(z);
        return;
    }
    if(!z)
    {
        Splay(y, 0);
        T[y].son[1]=0;
        PushUp(y);
        return;
    }
    Splay(y, 0);
    Splay(z, y);
    T[z].son[0]=0;
    PushUp(z);
    PushUp(y);
}

int GetPth(int p) //获得第p小的节点 并将其转移到根处
{
    if(!rt) return 0;
    int x=rt, ret=0;
    while(x)
    {
        if(p == T[T[x].son[0]].size+1)
            break;
        if(p>T[T[x].son[0]].size+1)
        {
            p-=T[T[x].son[0]].size+1;
            x=T[x].son[1];
        }
        else
            x=T[x].son[0];
    }
    Splay(x, 0);
    return x;
}

int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
{
    if(!rt) return 0;
    int x=rt, ret=0, y;
    while(x)
    {
        y=x;
        if(T[x].key <= key)
        {
            ret+=T[T[x].son[0]].size+1;
            x=T[x].son[1];
        }
        else
            x=T[x].son[0];
    }
    Splay(y, 0);
    return ret;
}

完全版:(支持相同值,支持区间删除,支持懒惰标记)

Struct Tree{

  int key, num, size, fa, son[2];

}

void PushUp(int x);

void PushDown(int x);

int Newnode(int key, int fa); //新建一个节点并返回

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To); //将x节点移动到To的子节点中

int GetPth(int p, int To); //返回第p小的节点 并移动到To的子节点中

int Find(int key); //返回值为key的节点 若无返回0 若有将其转移到根处

int Prev(); //返回根节点的前驱

int Succ(); //返回根结点的后继

void Insert(int key); //插入key值

void Delete(int key); //删除值为key的节点

int GetRank(int key); //获得值<=key的节点个数

void Delete(int l, int r); //删除值在[l, r]中的节点

模板:

int cnt, rt;
int Add[MAXN];

struct Tree{
    int key, num, size, fa, son[2];
}T[MAXN];

inline void PushUp(int x)
{
    T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num;
}

inline void PushDown(int x)
{
    if(Add[x])
    {
        if(T[x].son[0])
        {
            T[T[x].son[0]].key+=Add[x];
            Add[T[x].son[0]]+=Add[x];
        }
        if(T[x].son[1])
        {
            T[T[x].son[1]].key+=Add[x];
            Add[T[x].son[1]]+=Add[x];
        }
        Add[x]=0;
    }
}

inline int Newnode(int key, int fa) //新建一个节点并返回
{
    ++cnt;
    T[cnt].key=key;
    T[cnt].num=T[cnt].size=1;
    T[cnt].fa=fa;
    T[cnt].son[0]=T[cnt].son[1]=0;
    return cnt;
}

inline void Rotate(int x, int p) //0左旋 1右旋
{
    int y=T[x].fa;
    PushDown(y);
    PushDown(x);
    T[y].son[!p]=T[x].son[p];
    T[T[x].son[p]].fa=y;
    T[x].fa=T[y].fa;
    if(T[x].fa)
        T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
    T[x].son[p]=y;
    T[y].fa=x;
    PushUp(y);
    PushUp(x);
}

void Splay(int x, int To) //将x节点移动到To的子节点中
{
    while(T[x].fa != To)
    {
        if(T[T[x].fa].fa == To)
            Rotate(x, T[T[x].fa].son[0] == x);
        else
        {
            int y=T[x].fa, z=T[y].fa;
            int p=(T[z].son[0] == y);
            if(T[y].son[p] == x)
                Rotate(x, !p), Rotate(x, p); //之字旋
            else
                Rotate(y, p), Rotate(x, p); //一字旋
        }
    }
    if(To == 0) rt=x;
}

int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
{
    if(!rt || p > T[rt].size) return 0;
    int x=rt;
    while(x)
    {
        PushDown(x);
        if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num)
            break;
        if(p > T[T[x].son[0]].size+T[x].num)
        {
            p-=T[T[x].son[0]].size+T[x].num;
            x=T[x].son[1];
        }
        else
            x=T[x].son[0];
    }
    Splay(x, 0);
    return x;
}

int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{
    if(!rt) return 0;
    int x=rt;
    while(x)
    {
        PushDown(x);
        if(T[x].key == key) break;
        x=T[x].son[key > T[x].key];
    }
    if(x) Splay(x, 0);
    return x;
}

int Prev() //返回根节点的前驱 非重点
{
    if(!rt || !T[rt].son[0]) return 0;
    int x=T[rt].son[0];
    while(T[x].son[1])
    {
        PushDown(x);
        x=T[x].son[1];
    }
    Splay(x, 0);
    return x;
}

int Succ() //返回根结点的后继 非重点
{
    if(!rt || !T[rt].son[1]) return 0;
    int x=T[rt].son[1];
    while(T[x].son[0])
    {
        PushDown(x);
        x=T[x].son[0];
    }
    Splay(x, 0);
    return x;
}

void Insert(int key) //插入key值
{
    if(!rt)
        rt=Newnode(key, 0);
    else
    {
        int x=rt, y=0;
        while(x)
        {
            PushDown(x);
            y=x;
            if(T[x].key == key)
            {
                T[x].num++;
                T[x].size++;
                break;
            }
            T[x].size++;
            x=T[x].son[key > T[x].key];
        }
        if(!x)
            x=T[y].son[key > T[y].key]=Newnode(key, y);
        Splay(x, 0);
    }
}

void Delete(int key) //删除值为key的节点1个
{
    int x=Find(key);
    if(!x) return;
    if(T[x].num>1)
    {
        T[x].num--;
        PushUp(x);
        return;
    }
    int y=T[x].son[0];
    while(T[y].son[1])
        y=T[y].son[1];
    int z=T[x].son[1];
    while(T[z].son[0])
        z=T[z].son[0];
    if(!y && !z)
    {
        rt=0;
        return;
    }
    if(!y)
    {
        Splay(z, 0);
        T[z].son[0]=0;
        PushUp(z);
        return;
    }
    if(!z)
    {
        Splay(y, 0);
        T[y].son[1]=0;
        PushUp(y);
        return;
    }
    Splay(y, 0);
    Splay(z, y);
    T[z].son[0]=0;
    PushUp(z);
    PushUp(y);
}

int GetRank(int key) //获得值<=key的节点个数
{
    if(!Find(key))
    {
        Insert(key);
        int tmp=T[T[rt].son[0]].size;
        Delete(key);
        return tmp;
    }
    else
        return T[T[rt].son[0]].size+T[rt].num;
}

void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
{
    if(!Find(l)) Insert(l);
    int p=Prev();
    if(!Find(r)) Insert(r);
    int q=Succ();
    if(!p && !q)
    {
        rt=0;
        return;
    }
    if(!p)
    {
        T[rt].son[0]=0;
        PushUp(rt);
        return;
    }
    if(!q)
    {
        Splay(p, 0);
        T[rt].son[1]=0;
        PushUp(rt);
        return;
    }
    Splay(p, q);
    T[p].son[1]=0;
    PushUp(p);
    PushUp(q);
}

(经测NOI2004郁闷的出纳员 POJ3481 POJ2352 POJ1442)

速度相对来说都还不错,POJ这些都3~500ms,郁闷的出纳员900多ms

相关题解:

HNOI 2002 营业额统计

POJ 3481 splay

POJ 2352 splay

POJ 1442 splay

NOI2004 郁闷的出纳员

II 用于维护序列:(以序列下标为对象维护,相当于对区间操作)(能够完成线段树的操作及其不能完成的操作)

Struct Tree{

  int key, sum, size, fa, son[2];

}

支持操作:

void PushUp(int x);

void PushDown(int x);

int MakeTree(int l, int r, int a[]); //新建一个子树返回根节点

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To); //将x节点移动到To的子节点中

int Select(int p, int To); //将第p个数移动到To的子节点中 并返回该节点

int Find(int key); //返回值为key的节点 若无返回0 若有将其转移到根处

int Prev(); //返回根节点的前驱

int Succ(); //返回根结点的后继

void Insert(int p, int l, int r, int a[]) //将a[l .. r]的数插入到下标为p后面

void Delete(int l, int r); //删除区间[l, r]中的节点

int Query(int l, int r); //返回[l, r]的和

待补充。。

Size Balance Tree

  和上述两种二叉树比起来,SBT可能是最像真正平衡二叉树吧。

  SBT能够保证树的高度在lgn,这样对于插入,删除操作都能够准确保证时间复杂度在O(lgn)

  Maintain操作事实上理解起来也是挺简单的,至于证明参见CQF神牛的《SBT》

int cnt, rt;

struct Tree
{
    int key, size, son[2];
}T[MAXN];

inline void PushUp(int x)
{
    T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1;
}

inline int Newnode(int key)
{
    ++cnt;
    T[cnt].key=key;
    T[cnt].size=1;
    T[cnt].son[0]=T[cnt].son[1]=0;
    return cnt;
}

void Rotate(int p, int &x)
{
    int y=T[x].son[!p];
    T[x].son[!p]=T[y].son[p];
    T[y].son[p]=x;
    PushUp(x);
    PushUp(y);
    x=y;
}

void Maintain(int &x, int p) //维护SBT的!p子树
{
    if(T[T[T[x].son[p]].son[p]].size > T[T[x].son[!p]].size)
        Rotate(!p, x);
    else if(T[T[T[x].son[p]].son[!p]].size > T[T[x].son[!p]].size)
        Rotate(p, T[x].son[p]), Rotate(!p, x);
    else return;
    Maintain(T[x].son[0], 0);
    Maintain(T[x].son[1], 1);
    Maintain(x, 0);
    Maintain(x, 1);
}

inline int Prev() //返回比根值小的最大值 若无返回0
{
    int x=T[rt].son[0];
    if(!x) return 0;
    while(T[x].son[1])
        x=T[x].son[1];
    return x;
}

inline int Succ() //返回比根值大的最小值 若无返回0
{
    int x=T[rt].son[1];
    if(!x) return 0;
    while(T[x].son[0])
        x=T[x].son[0];
    return x;
}

void Insert(int key, int &x)
{
    if(!x) x=Newnode(key);
    else
    {
        T[x].size++;
        Insert(key, T[x].son[key > T[x].key]);
        Maintain(x, key > T[x].key);
    }
}

bool Delete(int key, int &x) //删除值为key的节点 key可以不存在
{
    if(!x) return 0;
    if(T[x].key == key)
    {
        if(!T[x].son[0])
        {
            x=T[x].son[1];
            return 1;
        }
        if(!T[x].son[1])
        {
            x=T[x].son[0];
            return 1;
        }
        int y=Prev();
        T[x].size--;
        return Delete(T[x].key, T[x].son[0]);
    }
    else
        if(Delete(key, T[x].son[key > T[x].key]))
        {
            T[x].size--;
            return 1;
        }
}

int GetPth(int p, int &x) //返回第p小的节点
{
    if(!x) return 0;
    if(p == T[T[x].son[0]].size+1)
        return x;
    if(p > T[T[x].son[0]].size+1)
        return GetPth(p-T[T[x].son[0]].size-1, T[x].son[1]);
    else
        return GetPth(p, T[x].son[0]);
}

int GetRank(int key, int &x) //找出值<=key的节点个数
{
    if(!x) return 0;
    if(T[x].key <= key)
        return T[T[x].son[0]].size+1+GetRank(key, T[x].son[1]);
    else
        return GetRank(key, T[x].son[0]);
}

相关题解:

POJ 3481 SBT做法

上述题均为用于测试平衡树基本操作的题目。

提高题:(暂时未写)

[NOI2005]维修数列

[POJ3580]SuperMemo

[HNOI2004]宠物收养所

时间: 2024-10-08 01:25:47

三大平衡树(Treap + Splay + SBT)总结+模板的相关文章

平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】

平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是将有向树的所有边看成无向边形成的树状图.树是一种递归的数据结构,所以我们研究树也是按照递归的方式去研究的. 2.什么是二叉树. 我们给出二叉树的递归定义如下: (1)空树是一个二叉树. (2)单个节点是一个二叉树. (3)如果一棵树中,以它的左右子节点为根形成的子树都是二叉树,那么这棵树本身也是二叉

luoguP3369[模板]普通平衡树(Treap/SBT) 题解

链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #incl

各种平衡树Treap/SBT/Avl/Splay tree

看了这么久的平衡树,是时候做个总结了. 以poj 3481为例,敲了四份代码,分别是Treap ,Size Balance Tree,Avl Tree,splay tree. 唯独少了红黑树T_T... 总的来说每种平衡树各有各的优点吧: Treap写起来简单上手也快如果熟练的话不到十分种可以敲完. SBT速度快名不虚传... Avl树高度平衡吧,不过实际的效果不尽如人意,可能是我实现的姿势不对吧/(ㄒoㄒ)/~~ splay tree各方面比较均衡,特别的伸展树在维护序列方面相对其它几种树优势

算法模板——平衡树Treap 2

实现功能:同平衡树Treap 1(BZOJ3224 / tyvj1728) 这次的模板有了不少的改进,显然更加美观了,几乎每个部分都有了不少简化,尤其是删除部分,这个参照了hzwer神犇的写法,在此鸣谢,然后,贴模板走人 1 var 2 i,j,k,l,m,n,head,tot:longint; 3 a,b,lef,rig,fix:array[0..100010] of longint; 4 function min(x,y:longint):longint; 5 begin 6 if x<y

P3391 【模板】文艺平衡树(Splay)新板子

P3391 [模板]文艺平衡树(Splay) 题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,

hiho 1325 : 平衡树&#183;Treap

#1325 : 平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? 小Ho:就是二叉排序树和堆啊,你看这两种数据结构都是构造了一个二叉树,一个节点有一个父亲和两个儿子. 如果用1..n的数组来存储的话,对于二叉树上的一个编号为k的节点,其父亲节点刚好是k/2.并且它的两个儿子节点分别为k*2和k*2+1,计算起来非常方便呢. 小Hi:没错,但是小Hi你知道有一种办

平衡树 Treap

//平衡树 Treap //维护一个堆使得随机权值小(大)的数始终在上方 //使用随机权值目的:防止出题人卡 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; struct uio{ int l,r,siz,num,rd,tim;

洛谷P3391 【模板】文艺平衡树(Splay)(FHQ Treap)

题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n−1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r

fhq treap ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : LibreOJ #104. 普通平衡树 #include <cstdio> #include <iostream> #include <algorithm> const int BUF = 12312323; char Buf[BUF], *buf = Buf; inline void read (int &now) { bool temp = false; for (now = 0; !isdigit (*buf); ++ buf) if (*