【BZOJ3551】Peaks加强版,主席树+kruskal重构+dfs序+倍增思想

传送门
写在前面:一道调了不止一上午的题目
思路:我们先来看一下神奇的kruskal重构树
这里写图片描述

图中红色的是原图的边权,黑色的是原图上的点

这样生成的树有一些十分优美的性质:

1.二叉树(好吧这题意义不大)

2.原树与新树两点间路径上边权(点权)的最大值相等

3.子节点的边权小于等于父亲节点(大根堆)

4.原树中两点之间路径上边权的最大值等于新树上两点的LCA的点权

这些神奇的性质可以解决一些图上的连通性问题。
对于这道题来说,我们就可以kruskal一下(排序+并查集),建出这样一颗二叉树。那么我们每次处理询问的时候,只需要从x一直往权值小于等于要求长度的祖先上蹦,最终所在祖先y的子树就是我们要求的点的范围,即子树上找第k大,这个我们是可以dfs序编号后用主席树来搞一下的。
总结一下就是说,我们先对边权升序排一遍,做成一棵有根的树,然后对这棵树进行dfs重新编号,并离散化原点权(即山的高度),倍增求祖先,然后对dfs后的编号建立主席树。处理询问时我们要一直往上蹦到符合条件的祖先,用主席树寻找第k大即可
注意:
1.这里求得是第k大,也就是第siz-k+1小的数
2.离散化,重编号这里很容易把人搞晕,推荐自己先写写步骤再开始码代码,我这里是先离散化原数据再dfs编号的。
3.倍增预处理和离散化操作在init函数里
4.由于是二叉树,所以采用父亲儿子表示法比较方便
5.题意感觉不是很清晰,这里让求的是第k大的权值……所以还要记录离散化后的数所对应的原权值(ID数组)
代码:

#include<bits/stdc++.h>
#define M 100010
using namespace std;
int n,m,q,lastans,cnt,tot;
int ID[M],belong[M<<1],h[M<<1],fa[M<<1][21],son[M<<1][2],Ls[M<<1],Rs[M<<1];
bool vis[M<<1];
struct Elite
{
    int u,v,w;
    bool operator <(const Elite other)const
    {
        return w<other.w;
    }
}eli[M*5];
struct Chairman_tree
{
    int ch[2],siz;
}a[M*50];
struct node
{
    int data,id;
    bool operator <(const node other)const
    {
        return data<other.data;
    }
}b[M];
int in()
{
    char ch=getchar();int t=0,f=1;
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return f*t;
}
int find(int x)
{
    if (x!=belong[x]) belong[x]=find(belong[x]);
    return belong[x];
}
void build(int now,int L,int R,int rt,int val)
{
    a[rt].siz=a[now].siz+1;
    if (L==R) return;
    int mid=(L+R)>>1;
    if (val<=mid)
        a[rt].ch[1]=a[now].ch[1],
        a[rt].ch[0]=++cnt,
        build(a[now].ch[0],L,mid,a[rt].ch[0],val);
    else
        a[rt].ch[0]=a[now].ch[0],
        a[rt].ch[1]=++cnt,
        build(a[now].ch[1],mid+1,R,a[rt].ch[1],val);
}
int get(int begin,int end,int L,int R,int k)
{
    if (begin==end) return end;
    int mid=(begin+end)>>1,t=a[a[R].ch[0]].siz-a[a[L].ch[0]].siz;
    if (k<=t)
        return get(begin,mid,a[L].ch[0],a[R].ch[0],k);
    else
        return get(mid+1,end,a[L].ch[1],a[R].ch[1],k-t);
}
void dfs(int x)
{
    vis[x]=1;
    if (x<=n) b[++tot].data=h[x],b[tot].id=x;
    Ls[x]=tot;
    if (son[x][0]) dfs(son[x][0]);
    if (son[x][1]) dfs(son[x][1]);
    Rs[x]=tot;
}
void init()
{
    for (int j=1;j<20;j++)
        for (int i=1;i<=cnt;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    for(int i=1;i<=n;i++) b[i].id=i,b[i].data=h[i];
    sort(b+1,b+n+1);
    for (int i=1;i<=n;i++)
        ID[i]=h[b[i].id],h[b[i].id]=i;
}
main()
{
    n=in();m=in();q=in();
    for (int i=1;i<=n;i++) h[i]=in();
    for (int i=1;i<=m;i++)
        eli[i].u=in(),eli[i].v=in(),eli[i].w=in();
    sort(eli+1,eli+m+1);
    cnt=n;
    for (int i=1;i<=n<<1;i++) belong[i]=i;
    for (int i=1;i<=m;i++)
    {
        int p=find(eli[i].u),q=find(eli[i].v);
        if (p!=q)
            fa[p][0]=++cnt,fa[q][0]=cnt,
            belong[p]=cnt,belong[q]=cnt,
            son[cnt][0]=p,son[cnt][1]=q,
            h[cnt]=eli[i].w;
    }
    fa[cnt][0]=cnt; 
    init();
    for (int i=cnt;i>=1;i--)
        if (!vis[i]) dfs(i); 
    cnt=n+1;
    int x,y,z;
    for (int i=1;i<=n;i++) build(i,1,n,i+1,b[i].data);
    while (q--)
    {
        if (lastans==-1) lastans=0;
        x=in()^lastans;
        y=in()^lastans;
        z=in()^lastans;
        for (int i=19;i>=0;i--)
            if (h[fa[x][i]]<=y) x=fa[x][i];
        int sz=a[Rs[x]+1].siz-a[Ls[x]+1].siz;
        if (sz<z) lastans=-1;
        else lastans=ID[get(1,n,Ls[x]+1,Rs[x]+1,sz-z+1)];
        printf("%d\n",lastans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值