zoukankan      html  css  js  c++  java
  • 【BZOJ3545/BZOJ3551】Peaks(ONTAK2010)-Kruskal重构树+主席树

    测试地址:Peaks原版Peaks加强版
    题目大意:一张n(105)个点m(5×105)条边的无向图,点和边都有权,q(5×105)个询问,每次询问从一个点v能仅通过走边权不超过x的边走到的所有点中,第k大的点权。原版不强制在线,加强版强制在线。
    做法:本题需要用到Kruskal重构树+主席树。
    注意到两点间路径最大值的最小值一定在最小生成树上,因此只有最小生成树上的边是有用的。
    在原版题中,不强制在线,我们就可以把询问按x从小到大排序,用Kruskal算法和平衡树启发式合并即可处理这些询问,时间复杂度为O(nlog2n)
    而在加强版中,强制在线使得我们不能使用上面的方法。注意到,上面的方法非常依赖于加边的顺序,那么有没有一种方法来描述Kruskal算法的过程,使得我们可以脱离时序进行询问呢?答案是肯定的,这时候就要引出Kruskal重构树这个东西了。
    建树的思想其实非常简单,在Kruskal算法中,每往最小生成树中加一条边,就新增一个点表示这条边,并向它连接的两个点所属的树的顶部连边,这样我们就建出了这棵树。这棵树有一些重要的性质:
    1.所有叶子节点都是原图中的点,其他点都为边。
    2.一个点所表示的边的边权,一定不比它儿子表示的边的边权小。
    3.两个叶子节点的LCA所表示的边的边权,为原图最小生成树中两点间的最大边权。
    这些性质都是很显然成立的,于是我们很自然地想到一个做法:
    利用性质2,每次询问,我们可以从点v向上倍增到最顶部的不大于x的点,那么以这个点为根的子树内所有叶子节点,就是从点v能经过不超过x的边权到达的点了,而询问一棵子树中的第k大值,立马想到在DFS序上用主席树维护,这样我们就解决了这一题,时间复杂度为O(nlogn)
    (然而非常有毒的一点是,以下这份代码在原版题中A了,而加了注释中的解密语句后在加强版题中RE了,而该题RE数比其他结果的数量都多得多……有待弄清情况)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,q,tot,totp,pos[200010],h[200010];
    int fa[200010][20]={0},ch[200010][2]={0},tim;
    int top[200010],topp[200010],siz[200010]={0},in[200010],out[200010];
    int rt[200010]={0},T=0,seg[2000010]={0},segch[2000010][2]={0},pp[100010];
    bool vis[200010]={0};
    struct edge
    {
        int a,b,x;
    }e[500010];
    struct forsort
    {
        int id,val;
    }f[200010];
    
    bool cmp(forsort a,forsort b)
    {
        return a.val<b.val;
    }
    
    bool cmpedge(edge a,edge b)
    {
        return a.x<b.x;
    }
    
    int read()
    {
        char c;
        int s=0;
        c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') s=s*10+c-'0',c=getchar();
        return s;
    }
    
    void init1()
    {
        n=read(),m=read(),q=read();
        for(int i=1;i<=n;i++)
        {
            f[i].id=i;
            f[i].val=read();
        }
    
        sort(f+1,f+n+1,cmp);
        tot=0;
        for(int i=1;i<=n;i++)
        {
            if (i==1||f[i].val!=f[i-1].val)
                pos[++tot]=f[i].val;
            h[f[i].id]=tot;
            top[i]=i;
            topp[i]=i;
        }
    }
    
    int find(int x)
    {
        int r=x,i=x,j;
        while(r!=top[r]) r=top[r];
        while(i!=r) j=top[i],top[i]=r,i=j;
        return r;
    }
    
    void merge(int x,int y,int p)
    {
        int fx=find(x),fy=find(y);
        top[fx]=fy;
        topp[fy]=p;
    }
    
    void dfs(int v)
    {
        in[v]=tim;
        siz[v]=0;
        for(int i=0;i<=1;i++)
        {
            if (!ch[v][i]) continue;
            dfs(ch[v][i]);
            siz[v]+=siz[ch[v][i]];
        }
        if (!ch[v][0]&&!ch[v][1])
            pp[++tim]=v,siz[v]=1;
        out[v]=tim;
    }
    
    void buildtree(int &no,int l,int r)
    {
        no=++T;
        if (l==r) return;
        int mid=(l+r)>>1;
        buildtree(segch[no][0],l,mid);
        buildtree(segch[no][1],mid+1,r);
    }
    
    void insert(int &no,int last,int l,int r,int x)
    {
        no=++T;
        seg[no]=seg[last];
        segch[no][0]=segch[last][0];
        segch[no][1]=segch[last][1];
        if (l==r) {seg[no]++;return;}
        int mid=(l+r)>>1;
        if (x<=mid) insert(segch[no][0],segch[last][0],l,mid,x);
        else insert(segch[no][1],segch[last][1],mid+1,r,x);
        seg[no]=seg[segch[no][0]]+seg[segch[no][1]];
    }
    
    int query(int last,int no,int l,int r,int k)
    {
        if (l==r) return l;
        int s=seg[segch[no][0]]-seg[segch[last][0]];
        int mid=(l+r)>>1;
        if (k<=s) return query(segch[last][0],segch[no][0],l,mid,k);
        else return query(segch[last][1],segch[no][1],mid+1,r,k-s);
    }
    
    void init2()
    {
        for(int i=1;i<=m;i++)
            e[i].a=read(),e[i].b=read(),e[i].x=read();
        sort(e+1,e+m+1,cmpedge);
        totp=n;
        for(int i=1;i<=m;i++)
            if (find(e[i].a)!=find(e[i].b))
            {
                int fx=find(e[i].a),fy=find(e[i].b);
                ++totp;
                h[totp]=e[i].x;
                fa[topp[fx]][0]=fa[topp[fy]][0]=totp;
                ch[totp][0]=topp[fx],ch[totp][1]=topp[fy];
                merge(e[i].a,e[i].b,totp);
            }
    
        tim=0;
        for(int i=1;i<=n;i++)
            if (!vis[topp[find(i)]])
            {
                vis[topp[find(i)]]=1;
                dfs(topp[find(i)]);
            }
    
        buildtree(rt[0],1,tot);
        for(int i=1;i<=n;i++)
            insert(rt[i],rt[i-1],1,tot,h[pp[i]]);
    
        for(int i=1;i<=18;i++)
            for(int j=1;j<=totp;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];
    }
    
    int findtop(int v,int x)
    {
        for(int i=18;i>=0;i--)
            if (fa[v][i]&&h[fa[v][i]]<=x)
                v=fa[v][i];
        return v;
    }
    
    void work()
    {
        int lastans=0;
        for(int i=1;i<=q;i++)
        {
            int v,x,k,t;
            v=read(),x=read(),k=read();
            //v^lastans,x^=lastans,k^=lastans;
            t=findtop(v,x);
            if (siz[t]<k)
            {
                printf("-1
    ");
                lastans=0;
                continue;
            }
            lastans=pos[query(rt[in[t]],rt[out[t]],1,tot,siz[t]-k+1)];
            printf("%d
    ",lastans);
        }
    }
    
    int main()
    {
        init1();
        init2();
        work();
    
        return 0;
    }
  • 相关阅读:
    一个简单的linux线程池(转-wangchenxicool)
    Linux下获得系统时间的C语言实现
    C语言实现简单线程池(转-Newerth)
    C语言实现Web客户端(转-kungstriving)
    C语言实现的Web服务器(转-kungstriving)
    linux和window下mkdir函数问题(转-锦曦月)
    linux C 获取当前目录的实现(转-Blossom)
    linux C之access函数(转-追梦的小鸟)
    Linux C 创建目录函数mkdir相关(转-清新居士)
    50个C/C++源代码网站(转-清风小阁)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793343.html
Copyright © 2011-2022 走看看