【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\n");
            lastans=0;
            continue;
        }
        lastans=pos[query(rt[in[t]],rt[out[t]],1,tot,siz[t]-k+1)];
        printf("%d\n",lastans);
    }
}

int main()
{
    init1();
    init2();
    work();

    return 0;
}
posted @ 2018-06-08 13:08  Maxwei_wzj  阅读(139)  评论(0编辑  收藏  举报