正文

线段树的应用PKU29852008-08-21 13:00:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/lqf110/37755.html

分享到:

/////////////////////////////////////////
//作品名称:并查集及线段树应用
//作者姓名:满天飞
//作品时间:2008-8-18
//最后修改:2008-8-18
//Q Q 号码:280282813
//版权所有,翻版必究
////////////////////////////////////////
//写得要死终于过了
#include<iostream>
using namespace std;
typedef struct
{
    int lbound;   //左界
    int rbound;   //右界
    int left;   //左子树
    int right;   //右子树
    int ct;    //覆盖次数
}TreeNode;
class SegmentTree
{
    private:
        int nodenum;
        TreeNode* tree;
    public:
        SegmentTree(int);
        virtual ~SegmentTree(){delete[]tree;}
 //构建树
        void build(int, int);
 //插入一个数
        void insert(int);
 //删除一个数
        void erase(int);
 //查询第i大的数
        int search(int);
};
SegmentTree::SegmentTree(int a)
{
    tree = new TreeNode[2*a+10];
    memset(tree, 0, sizeof(TreeNode)*(2*a+10));
    nodenum = 0;
    build(a, 1);  
}
//采用先序从大到小递归构建
void SegmentTree::build(int a, int b)
{
    nodenum++;
    int v;
    v = nodenum;
    tree[v].lbound = a;
    tree[v].rbound = b;
    if((a-b)>1){
        tree[v].left = nodenum+1;
        build(a, (a+b)/2);
        tree[v].right = nodenum+1;
        build((a+b)/2, b);
    }
}
//插入某个数到叶子结点为止
void SegmentTree::insert(int a)
{
    int mid,f;
    f = 1;
    while((tree[f].lbound-tree[f].rbound) > 1){
  //对遍历过的结点依次增加覆盖次数
        tree[f].ct++;
  //取区间中值
        mid = (tree[f].lbound+tree[f].rbound)>>1;
  //比中值要大入左子树
        if(a > mid)
            f = tree[f].left;
  //否则进左子树
        else f = tree[f].right;
    }
 //叶子结点覆盖次数增加
    tree[f].ct++;
}
//类似插入操作
void SegmentTree::erase(int a)
{
    int mid, f;
    f = 1;
    while((tree[f].lbound-tree[f].rbound) > 1){
        tree[f].ct--;
        mid = (tree[f].lbound+tree[f].rbound)>>1;
        if(a > mid)
            f = tree[f].left;
        else f = tree[f].right;
    }
    tree[f].ct--;
}
//此树是从大到小排列,1是没有插入到树上的
int SegmentTree::search(int a)
{
    int f, l, r;
    f = 1;
 //此树的插入的数比所要查询的少则返回1
    if(tree[f].ct < a)
        return 1;
    else{
        while((tree[f].lbound-tree[f].rbound) > 1){
            l = tree[f].left;
            r = tree[f].right;
   //查询的第i个数比左子树存在的数的个数要多则进入右子树
            if(a > tree[l].ct){
    //减去左子树上的个数
                a = a-tree[l].ct;
                f = r;
            }
   //进左子树
            else f = l;
        }
    }
 //返回叶子结点的左边界
    return tree[f].lbound;
}
class Set
{
    public:
        int* ary;
        int length;
 //查找某个数的根结点
        int Find(int index);
 //合并两个数
        void Union(int first, int second);
        Set(int n);
        virtual ~Set(){delete[]ary;}
};
Set::Set(int n)
{
    int i;
    ary = new int[n+10];
    length = n;
    for(i = 1; i <= n; ++i)
        ary[i] = -1;
}
//将每次查找过的路线上的结点全部都指向根结点
int Set::Find(int index)
{
    int i, j, t;
    if(index <= 0 || index > length) return -1;
 //查找index元素的根结点
    for(i = index; ary[i] > 0; i = ary[i]);
 //将这条路线上的所有元素全部指向根结点
    for(j = index; j != i; j = t){
        t = ary[j];
        ary[j] = i;
    }
    return i;
}
//树的根结点保存树的元素个数,以负值的形式保存
void Set::Union(int f, int s)
{
 //如果第i棵树的元素比第j棵树的元素要多则将j加入到i中
    if(ary[f] <= ary[s]){
        ary[f] += ary[s];
        ary[s] = f;
    }
 //否则将i加入到j中
    else{
        ary[s] += ary[f];
        ary[f] = s;
    }
}
//算法:
//1.删除合并前的大小.
//2.插入合并后的大小.
//3.查询即可.
int main(int argv, char* argc[])
{
    int vex, opt, i;
    int optflag, f, s;
    int fsize, ssize, froot, sroot;
    cin>>vex>>opt;
    Set set(vex+10);
    SegmentTree tree(vex);
    for(i = 0; i < opt; ++i)
    {
        scanf("%d", &optflag);
        if(optflag == 0)
        {
            scanf("%d%d", &f, &s);
            froot = set.Find(f);
            sroot = set.Find(s);
            if(froot != sroot)
            {
    //得到树的大小
                fsize = -set.ary[froot];
                ssize = -set.ary[sroot];
    //将合并前的树大小删除
                if(fsize > 1)
                    tree.erase(fsize);
                if(ssize > 1)
                    tree.erase(ssize);
    //合并后的树大小插入
                tree.insert(fsize+ssize);
    //合并两棵树
                set.Union(froot, sroot);
            }
        }
        else
        {
            scanf("%d", &f);
            cout<<tree.search(f)<<endl;
        }
    }
    return 0;
}

阅读(2886) | 评论(2)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册