BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

转载来自于:http://blog.csdn.net/PoPoQQQ/article/details/41348785

弱真不知道这个题解怎么写。


题目大意:同3545 强制在线

3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953

强制在线没法排序 启发式合并也就用不了了

Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题

这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上

比如说样例的树就建成了这样

BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树_第1张图片

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

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

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

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

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

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

于是对于每个询问 我们从这个询问向上倍增寻找深度最小的点权小于等于x的点 易证这个节点的子树就是v所能到达的所有点

DFS序+可持久化线段树直接搞就行



代码(本人):

#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<bitset>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<cstdlib>
#include<cmath>
#define PI 2*asin(1.0)
#define LL long long
#define pb push_back
#define pa pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a))
#define lson lr<<1,l,mid
#define rson lr<<1|1,mid+1,r
#define bug(x) printf("%d++++++++++++++++++++%d\n",x,x)
#define key_value ch[ch[root][1]][0]
const int  MOD = 1000000007;
const int N = 2e5+15;
const int maxn = 5e5+1000;
const int letter = 130;
const int INF = 1e17;
const double pi=acos(-1.0);
const double eps=1e-8;
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,q,h[N],val[N],father[N],fnp,ps[N];
int tot,head[N],deep[N],fa[N][20],dis[N][20];
int l[N],r[N],top,dfn[N],vis[N],bin[20];
int root[N],ls[N*20],rs[N*20],sum[N*20],siz;
struct node{
    int x,y,val;
    bool operator <(const node &a)const{
        return val<a.val;
    }
}qu[maxn];
struct edges{
    int to,next;
}e[N];
int Find(int x){
    if(x==father[x]) return x;
    return father[x]=Find(father[x]);
}
void init(){
    siz=top=tot=0,clr(head,-1);
    for(int i=1;i<=2*n;i++) father[i]=i;
    bin[0]=1;
    for(int i=1;i<=18;i++) bin[i]=bin[i-1]<<1;
}
int findmax(int x,int val){
    for(int i=18;i>=0;i--) if(deep[x]>=bin[i]&&dis[x][i]<=val) x=fa[x][i];
    return x;
}
void addedges(int u,int v){
    e[tot].to=v,e[tot].next=head[u],head[u]=tot++;
}
void dfs(int x){
    vis[x]=1;
    dfn[++top]=x,l[x]=top;
    for(int i=head[x];i!=-1;i=e[i].next){
        int to=e[i].to;
        deep[to]=deep[x]+1;
        fa[to][0]=x;
        dis[to][0]=val[x];
        dfs(to);
    }
    r[x]=top;
}
void insert(int l,int r,int x,int &y,int v){
    y=++siz;
    sum[y]=sum[x]+1;
    if(l==r) return;
    ls[y]=ls[x],rs[y]=rs[x];
    int mid=(l+r)>>1;
    if(v<=mid) insert(l,mid,ls[x],ls[y],v);
    else insert(mid+1,r,rs[x],rs[y],v);
}
int query(int l,int r,int a,int b,int k){
    if(l==r) return l;
    int mid=(l+r)>>1;
    if(sum[ls[b]]-sum[ls[a]]>=k) return query(l,mid,ls[a],ls[b],k);
    else return query(mid+1,r,rs[a],rs[b],k-(sum[ls[b]]-sum[ls[a]]));
}
int main(){
    n=read(),m=read(),q=read();
    fnp=n;///sum node
    for(int i=1;i<=n;i++) h[i]=read(),ps[i]=h[i];
    sort(ps+1,ps+n+1);
    for(int i=1;i<=n;i++) h[i]=lower_bound(ps+1,ps+n+1,h[i])-ps;
    for(int i=0;i<m;i++){
        qu[i].x=read(),qu[i].y=read(),qu[i].val=read();
    }
    sort(qu,qu+m);
    init();
    /// kruskal chonggou
    for(int i=0;i<m;i++){
        int xx=Find(qu[i].x),yy=Find(qu[i].y);
        if(xx!=yy){
            fnp++;
            father[xx]=father[yy]=fnp;
            addedges(fnp,xx),addedges(fnp,yy);
            val[fnp]=qu[i].val;
        }
    }/// kruskal tree
    for(int i=1;i<=n;i++){
        if(!vis[i]) dfs(Find(i));
    }
    for(int j=1;j<=18;j++)
    for(int i=1;i<=fnp;i++){
        fa[i][j]=fa[fa[i][j-1]][j-1];
        dis[i][j]=max(dis[i][j-1],dis[fa[i][j-1]][j-1]);
    }
    for(int i=1;i<=top;i++){ ///dfs xu
        int x=dfn[i];
        if(x<=n) insert(1,n,root[i-1],root[i],h[x]);
        else root[i]=root[i-1];
    }
    int ans=-1;
    while(q--){
        int x,v,k,aim;
        x=read(),v=read(),k=read();
        if(ans!=-1) x=x^ans,v=v^ans,k=k^ans;
        aim=findmax(x,v);/// root
        int la=root[l[aim]],ra=root[r[aim]];
        if(sum[ra]-sum[la]<k) ans=-1;
        else ans=query(1,n,la,ra,sum[ra]-sum[la]+1-k),ans=ps[ans];
        printf("%d\n",ans);
    }
    return 0;
}



你可能感兴趣的:(最小生成树,kruskal重构树)