「BZOJ3545」「ONTAK2010」Peaks

「BZOJ3545」「ONTAK2010」Peaks

题目传送门

题目大意:

给定一个 \(n\) 个点,\(m\) 条边的带点权边权无向图,有 \(q\) 次询问,每次询问从 \(v\) 点出发,经过边权 \(\le x\) 的边能够经过的第 \(k\) 大点权,若不足 \(k\) 个输出 \(-1\)


离线似乎可以使用类似于「HNOI2010」永无乡的线段树合并/平衡树启发式合并解法。

我们在这里只讨论在线做法。

首先考虑如何处理边权 \(\le x\) 这一条件,显然,我们在最小生成树上走是最优的。

所以我们可以考虑将边权从小到大排序,构建一棵Kruskal重构树,这样我们在查询的时候就只需要在这颗重构树上倍增即可求出我们需要的特殊点。

什么是Kruskal重构树?

就是使用Kruskal算法求最小生成树,当每次合并两个连通块的时候,新建一个节点作为两个连通块的父亲,让这条边的边权作为这个新点的点权,这样最后会形成一棵二叉树的结构,而这棵树有这样一个性质:非叶点权从下到上递增

所以我们在上面才可以倍增求解。

然后考虑后一个条件:第 \(k\) 大点权。

我们发现,这实际上就是让我们在以特殊点为根的子树的叶节点中求出第 \(k\) 大点权,这个利用在 \(\texttt{DFS}\) 序上建立可持久化线段树就可以轻松解决。

至此,本题完结。

注意一些细节以及空间优化、常数优化。

贴代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
char buf[1<<24],*p1=buf,*p2=buf;
char getch(){
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++;
}
int read(){
    int num=0;char ch=getch();
    while(!isdigit(ch)) ch=getch();
    while(isdigit(ch)) num=num*10+ch-'0',ch=getch();
    return num;
}
int a[maxn],b[maxn];
struct cc{
    int u,v,w;
    bool operator<(const cc &h)const{
        return w<h.w;
    }
}e[maxn*5];
int num=0;
int rt[maxn],ls[maxn*20],rs[maxn*20],sum[maxn*20],cnt;
void update(int &u,int &pos,int l,int r,int &t){
    u=++cnt;
    ls[u]=ls[pos],rs[u]=rs[pos],sum[u]=sum[pos]+1;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(t<=mid) update(ls[u],ls[pos],l,mid,t);
    else update(rs[u],rs[pos],mid+1,r,t);
}
int query(int &ll,int &rr,int l,int r,int k){
    if(sum[rr]<k) return 0;
    if(l==r) return l;
    int x=sum[rs[rr]]-sum[rs[ll]];
    int mid=(l+r)>>1;
    if(k<=x) return query(rs[ll],rs[rr],mid+1,r,k);
    else return query(ls[ll],ls[rr],l,mid,k-x);
}//主席树部分
int f[maxn][20],mx[maxn][20];
int ch[maxn][2],tot;
int fa[maxn];
int getfa(int t){
    return fa[t]==t?t:fa[t]=getfa(fa[t]);
}
int dfn[maxn],tim;
int siz[maxn];
int n,m,q;
void dfs(int &u){
    dfn[u]=++tim,siz[u]=1;
    if(u<=n) update(rt[dfn[u]],rt[dfn[u]-1],1,num,a[u]);//实点
    else rt[dfn[u]]=rt[dfn[u]-1];//虚点 
    for(int i=0;i<=1;++i){
        int v=ch[u][i];
        if(!v) continue;
        f[v][0]=u,mx[v][0]=a[u];
        dfs(v);siz[u]+=siz[v];
    }
}
inline void solve(int &x,int &d){
    for(int i=18;i>=0;--i){
        if(f[x][i]&&mx[x][i]<=d) x=f[x][i];
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    n=read(),m=read(),q=read();
    for(int i=1;i<=n;++i){
        b[i]=read(),a[i]=b[i];
        fa[i]=i;
    }
    b[0]=-1;
    for(int i=1;i<=m;++i){
        int a=read(),b=read(),c=read();
        e[i]=(cc){a,b,c};
    }   
    tot=n;
    sort(e+1,e+m+1);
    for(int i=1;i<=m;++i){
        int fu=getfa(e[i].u),fv=getfa(e[i].v);
        if(fu==fv) continue;
        ++tot;fa[tot]=fa[fu]=fa[fv]=tot;
        b[tot]=a[tot]=e[i].w;
        ch[tot][0]=fv,ch[tot][1]=fu;
    }
    sort(b+1,b+n+1);
    num=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i)
        a[i]=lower_bound(b+1,b+num+1,a[i])-b;//离散化
    dfs(tot);
    for(int j=1;j<=18;++j){
        for(int i=1;i<=tot;++i){
            f[i][j]=f[f[i][j-1]][j-1];
            mx[i][j]=max(mx[i][j-1],mx[f[i][j-1]][j-1]);
        }
    }//倍增预处理 
    for(int i=1;i<=q;++i){
        int aa=read(),bb=read(),cc=read();
        solve(aa,bb);
        cout<<b[query(rt[dfn[aa]-1],rt[dfn[aa]+siz[aa]-1],1,num,cc)]<<'\n';
    }
    return 0;
}
posted @ 2019-11-06 21:48  Henry__Huang  阅读(192)  评论(0编辑  收藏  举报