UVALive 5031 Graph and Queries (Treap)

删除边的操作不容易实现一般就是先离线然后逆序来做。

逆序就变成了合并,用并存集判断连通,用Treap树来维护一个连通分量里的名次。

Treap = Tree + Heap。就是用一个随机的优先级来平衡树。

名次查询需要维护树的结点数量,假设当前在u点,u的左子树有n个结点,那么u的就是以u为根的树上第n+1小的。

如果查询的不是n+1,那么根据结点数量判断一下在哪颗子树上,然后去查询。

树的合并就将结点数少的树上的点往结点数多的树里面插,然后删掉结点少的树。

修改权值就分解成删除点和插点。

写的时候要分清哪些指针本身是要修改的,要用引用。哪些指针可能是NULL,不应该访问。

试过将null设定为常指针,快了25ms,用内存池模拟new 分配内存,快了50ms,但是结点数要开到maxn的两倍,(在这RE了很多次)。

如果new 的效率足够的话还是不要用内存池了,搞不好就RE了。

这题刷新了挂题发数,不过总算是会写Treap了。

#include<bits/stdc++.h>
using namespace std;

struct Node
{
    Node *ch[2];
    int v,r,s;
    void maintain()
    {
        s = 1+ch[0]->s+ch[1]->s;
    }
};

Node *const null = new Node();

inline Node *newNode(int x)
{
    Node* t = new Node();
    t->ch[0]=t->ch[1] = null;
    t->s = 1; t->r = rand(); t->v = x;
    return t;
}

void rot(Node*&o,int d)
{
    Node*t = o->ch[d^1]; o->ch[d^1] = t->ch[d]; t->ch[d] = o;
    o->maintain(); t->maintain();
    o = t;
}

void inst(Node*&o,int x)
{
    if(o==null){
        o = newNode(x);
    }else {
        int d = x > o->v ? 1:0;
        inst(o->ch[d],x);
        if(o->ch[d]->r > o->r) rot(o,d^1);
    }
    o->maintain();
}

inline int tcmp(int a,int b)
{
    if(a == b) return -1;
    return a > b? 0 : 1;
}

void rmov(Node*&o,int x)
{
    //if(o == null) return;
    int d = tcmp(o->v,x);
    if(~d){
        rmov(o->ch[d],x);
    }else {
        Node*lc = o->ch[0],*rc = o->ch[1];
        if(lc!=null &&rc != null){
            int d2 = lc->r > rc->r? 1:0;
            rot(o,d2); rmov(o->ch[d2],x);
        }else {
            Node *t = o;
            if(lc == null) o = rc;
            else o = lc;
            delete t;
        }
    }
    if(o != null) o->maintain();
}

const int maxc = 5e5+5, maxn = 2e4+5, maxm = 6e4+5;
struct Cmd
{
    char tp;
    int x,p;
}cmd[maxc];

int n,m,wei[maxn],fro[maxm],to[maxm],rmvd[maxm];

int pa[maxn];
int fdst(int x){ return x==pa[x]?x:pa[x]=fdst(pa[x]); }

Node *rt[maxn];

//k>0
int kth(Node*o,int k)
{
    if(o == null || k <= 0 || k > o->s) return 0; //
    int s = o->ch[1]->s;
    if(k == s+1) return o->v;
    if(k <= s) return kth(o->ch[1],k);
    return kth(o->ch[0],k-s-1);
}

void mgto(Node*&u,Node*&v)
{
    if(u->ch[0] != null) mgto(u->ch[0],v);
    if(u->ch[1] != null) mgto(u->ch[1],v);
    inst(v,u->v);
    delete u;
    u = null;
}

void rmvTree(Node*&o)
{
    if(o->ch[1] != null) rmvTree(o->ch[1]);
    if(o->ch[0] != null) rmvTree(o->ch[0]);
    delete o;
    o = null;
}

inline void addEdge(int i)
{
    int u = fdst(fro[i]), v = fdst(to[i]);
    if(u != v){
        if(rt[u]->s < rt[v]->s){
            pa[u] = v;
            mgto(rt[u],rt[v]);
        }else {
            pa[v] = u;
            mgto(rt[v],rt[u]);
        }
    }
}

int qct;
long long qtot;

inline void qry(int x,int p)
{
    if(p>0){
        qtot+=kth(rt[fdst(x)],p);
    }
    qct++;
}

inline void chgw(int x,int p)
{
    int u = fdst(x);
    rmov(rt[u],wei[x]);
    inst(rt[u],wei[x]=p);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int kas = 0;
    null->s = 0; null->ch[0] = null->ch[1] = null;
    fill(rt,rt+maxn,null);
    while(~scanf("%d%d",&n,&m)&&n){
        for(int i = 1; i <= n; i++) scanf("%d",wei+i);
        for(int i = 1; i <= m; i++) scanf("%d%d",fro+i,to+i);
        kas++;

        int c = 0;
        while(true){
            char tp; int x,p;
            scanf(" %c",&tp);
            if(tp == ‘E‘) break;
            scanf("%d",&x);
            if(tp == ‘D‘) rmvd[x] = kas;
            else if(tp == ‘Q‘) scanf("%d",&p);
            else if(tp == ‘C‘) {
                p = wei[x];
                scanf("%d",wei+x);
            }
            cmd[c++] = {tp,x,p};
        }

        for(int i = 1; i <= n; i++){
            pa[i] = i;
            if(rt[i] != null) rmvTree(rt[i]);
            rt[i] = newNode(wei[i]);
        }
        for(int i = 1; i <= m; i++) if(rmvd[i] != kas) addEdge(i);

        qtot = qct = 0;
        while(c--){
            Cmd &cq = cmd[c];
            if(cq.tp == ‘Q‘) qry(cq.x,cq.p);
            else if(cq.tp == ‘C‘) chgw(cq.x,cq.p);
            else if(cq.tp == ‘D‘)addEdge(cq.x);
        }
        printf("Case %d: %.6lf\n",kas,qtot/(double)qct);
    }
    return 0;
}
时间: 2024-10-15 02:15:23

UVALive 5031 Graph and Queries (Treap)的相关文章

UVA 1479 Graph and Queries (Treap)

题意:给一个无向图,再给一系列操作(以下3种),输出最后的平均查询结果. (1)D X 删除第x条边. (2)Q X k  查询与点X相连的连通分量中第k大的点的权值. (3)C X v  将点X的权值改为v. 吐槽:第一次写的人儿,WA,MLE,TLE各种惨.而且还好我写过splay,不然坑得更惨.耗时整整一天半在查错. 思路: 第一点,由于需要删除边,不是很方便,所以可以先将所有操作存起来,反序来操作,这样删边就变成加边,方便了不少.每次加边时若两点不属于同个连通分量,就将点少的整个连通分量

UVaLive 5031 Graph and Queries (Treap)

Graph and Queries Description You are given an undirected graph with N vertexes and M edges. Every vertex in this graph has an integer value assigned to it at the beginning. You’re also given a sequence of operations and you need to process them as r

uvalive 5031 Graph and Queries 名次树+Treap

题意:给你个点m条边的无向图,每个节点都有一个整数权值.你的任务是执行一系列操作.操作分为3种... 思路:本题一点要逆向来做,正向每次如果删边,复杂度太高.逆向到一定顺序的时候添加一条边更容易.详见算法指南P235. 1 #include<cstdlib> 2 3 struct Node 4 { 5 Node *ch[2]; // 左右子树 6 int r; // 随机优先级 7 int v; // 值 8 int s; // 结点总数 9 Node(int v):v(v) 10 { 11

UVALive 7261 Xiongnu&#39;s Land(二分)

题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5273 思路:二分位置(无需考虑总坐标,仅考虑横坐标即可),使得2*area >= sum,在满足该条件的情况下,尽量右移使得左侧面积尽量大. #include<cstdio> #include<cstring> #include<

Codeforces Round #216 (Div. 2) E. Valera and Queries (BIT)

题目大意: 给出很多条分布在 x 轴上的线段. 然后给出很多点集,问这些点集分布在多少条不同的线段上. 思路分析: 把点集分散成若干条线段. 如果点集做出的线段包含了某一条给出的线段的话,也就是说这个点集上不会有点在这条线段上. 所以我们就是求出 点集做出的线段包含了多少个给出的线段就可以了. 那么也就是比较l r的大小,排序之后用BIT #include <cstdio> #include <iostream> #include <algorithm> #includ

GPS-Graph Processing System Graph Coloring算法分析 (三)

Graph coloring is the problem of assigning a color to each vertex of an undirected graph such that no two adjacent vertices have the same color. We implement the greedy algorithm from Scalable parallel graph coloring algorithms. The algorithm iterati

关于平衡树(Treap)

平衡树是什么? 其实平衡树就是支持旋转的二叉查找树. 什么是二叉查找树呢? 其实就是满足(左子树(全部节点) < 根 < 右子树(全部节点))的一棵树,比如↓ (注意并不是每个节点都要是满的,也就是说它不一定是一棵完全二叉树) 那么二叉查找树有什么用呢? 假如我们要查询第k大的数,我们从根节点开始往下走,假如左子树大小大于等于k,我们就进入左子树去找:如果左子树大小等于(k-1),就输出当前节点:假如左子树大小小于(k-1),我们就进入右子树找排名为(k-(左子树大小+1))的数. 这样由于树

LA 5031 Graph and Queries —— Treap名次树

离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1logn2),由于合并之后原本结点数少的子树结点数至少翻倍,所以每个结点最多被插入 logn 次,故总时间复杂度为 O(n log2n)  . 注意细节处理,代码如下: 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstr

bzoj3224(treap)

3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1844  Solved: 727 [Submit][Status] Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 1. 插入x数 2. 删除x数(若有多个相同的数,因只删除一个) 3. 查询x数的排名(若有多个相同的数,因输出最小的排名) 4. 查询排名为x的数 5. 求x的前驱(前驱定义为小于x,