【BZOJ3545 & LGOJ4197】Peaks(Kruskal重构树 主席树)

时间:2019-11-04
本文章向大家介绍【BZOJ3545 & LGOJ4197】Peaks(Kruskal重构树 主席树),主要包括【BZOJ3545 & LGOJ4197】Peaks(Kruskal重构树 主席树)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目链接

大意

给出有\(N\)个点\(M\)条边的一张图,其中每个点都有一个High值,每条边都有一个Hard值。
再给出\(Q\)个询问:\(v\) \(x\) \(k\)
每次询问查询从点\(v\)出发,只经过Hard值小于等于\(x\)的边能到达的点中,第\(k\)大的High值。

思路

考虑Kruskal重构树:
在Kruskal算法求最小生成树的时候,每次加边将该边化成一个点,该点的点权值是原边权值。
然后用该点与两个连通块连边。
如图:

变为:

(左侧为点编号,右侧为点权)
这样重建一颗树后,可以发现其是一个大根堆。(根权最大)
然后我们就可以用倍增的思想在\(O(log(N))\)的时间内求出对于一对\((v,x)\)的上界。
(小注:上界指以该上界为根的子树内的点都可以被这对\((v,x)\)访问到)

在用Kruskal重构后,我们就可以将该问题变为求一颗子树内第\(K\)大的点权值。
对于这个问题,我们可以用主席树维护前缀的权值线段树,查询一颗子树时用DFN序就行了。

代码

附上一篇很慢的代码

#include<map>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXK=25;
const int MAXN=200005;
const int MAXM=500005;
int N,M,Q,Cnt,NCnt,PCnt,Root[MAXN];
int A[MAXN],Fa[MAXN],Val[MAXN];
int Dfn[MAXN],Tid[MAXN],Siz[MAXN];
int fa[MAXN][MAXK],Vat[MAXN][MAXK];
struct Edge{int x,y,z;}s[MAXM];
struct Quer{int p,x,k;}q[MAXM];
struct Node{int num,ch[2],l,r;}t[MAXM*120];
bool cmp(Edge X,Edge Y){return X.z<Y.z;}
int Find(int x){return Fa[x]==x?x:Fa[x]=Find(Fa[x]);}
int Len,B[MAXN],E[MAXN],ValCnt;
vector<int>P[MAXN];
map<int,int>Mp;
void Val_Init(){
    sort(B+1,B+Len+1);B[0]=-1;
    for(int i=1;i<=Len;i++)
        if(B[i]!=B[i-1]){
            Mp[B[i]]=++ValCnt;
            E[ValCnt]=B[i];
        }
    for(int i=1;i<=Cnt;i++)A[i]=Mp[A[i]];
}
void DFS(int u){
    Dfn[u]=++NCnt;
    Tid[NCnt]=u;Siz[u]=1;
    int size=P[u].size();
    for(int i=0;i<size;i++){
        int v=P[u][i];
        fa[v][0]=u;DFS(v);
        Vat[v][0]=max(Val[v],Val[u]);
        Siz[u]+=Siz[v];
    }
}
void Build(int &rt,int l,int r){
    rt=++PCnt;
    t[rt].l=l;t[rt].r=r;
    if(l==r)return ;
    int mid=(l+r)/2;
    Build(t[rt].ch[0],l,mid);
    Build(t[rt].ch[1],mid+1,r);
}
void Insert(int &rt,int ort,int p,int val){
    rt=++PCnt;t[rt]=t[ort];t[rt].num++;
    if(t[rt].l==t[rt].r)return ;
    int mid=(t[rt].l+t[rt].r)>>1;
    if(p<=mid)Insert(t[rt].ch[0],t[ort].ch[0],p,val);
    else Insert(t[rt].ch[1],t[ort].ch[1],p,val);
}
int Find(int rt1,int rt2,int k){
    if(t[rt1].l==t[rt1].r)return t[rt1].l;
    int num=t[t[rt1].ch[0]].num-t[t[rt2].ch[0]].num;
    if(num>=k)return Find(t[rt1].ch[0],t[rt2].ch[0],k);
    return Find(t[rt1].ch[1],t[rt2].ch[1],k-num);
}
int Get(int p,int x){
    for(int i=20;i>=0;i--)
        if(Vat[p][i]<=x&&fa[p][i])
            p=fa[p][i];
    return p;
}
void Init(){
    sort(s+1,s+M+1,cmp);Cnt=N;
    for(int i=1;i<=M;i++){
        int x=Find(s[i].x);
        int y=Find(s[i].y);
        if(x==y)continue;
        Fa[x]=++Cnt;Fa[y]=Cnt;
        Val[Cnt]=s[i].z;
        P[Cnt].push_back(x);
        P[Cnt].push_back(y);
    }
    Val_Init();
    for(int i=1;i<=Cnt;i++)
        if(Fa[i]==i)DFS(i);
    for(int k=1;k<=20;k++)
        for(int i=1;i<=Cnt;i++){
            fa[i][k]=fa[fa[i][k-1]][k-1];
            Vat[i][k]=max(Vat[i][k-1],Vat[fa[i][k-1]][k-1]);
        }
    PCnt=-1;
    Build(Root[0],1,ValCnt);
    for(int i=1;i<=Cnt;i++){
        if(A[Tid[i]]==1){
            Root[i]=Root[i-1];
            continue;
        }
        Insert(Root[i],Root[i-1],A[Tid[i]],1);
    }
}
int main(){
    scanf("%d%d%d",&N,&M,&Q);
    for(int i=1;i<=N;i++)
        scanf("%d",&A[i]),B[++Len]=A[i];B[++Len]=0;
    for(int i=1;i<=2*N;i++)Fa[i]=i;
    for(int i=1;i<=M;i++)
        scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].z);
    for(int i=1;i<=Q;i++)
        scanf("%d%d%d",&q[i].p,&q[i].x,&q[i].k);
    Init();
    for(int i=1;i<=Q;i++){
        int Tp=Get(q[i].p,q[i].x);
        int rt1=Root[Dfn[Tp]+Siz[Tp]-1],rt2=Root[Dfn[Tp]-1];
        int num=t[rt1].num-t[rt2].num;
        if(num<q[i].k){
            printf("-1\n");
            continue;
        }
        int ret=Find(rt1,rt2,num-q[i].k+1);
        printf("%d\n",E[ret]);
    }
}

原文地址:https://www.cnblogs.com/ftotl/p/11791525.html