逛森林

题目链接

看到区间连边最短路,考虑优化连边。发现直接重链剖分线段树时空分别是$O(m\log^3n)$和$O(m\log^2n)$的,被卡掉了。考虑树上倍增,用类似ST表的方法优化连边,更优。

引用别人题解的一段话:优化连边通常就是考虑一种数据结构$S$,这个$S$一般具有分治结构,且$S$的每一个节点都是图上的一个节点。建立一个入$S$和出$S$,其中出$S$的边由分治结构的子区间指向父亲,入$S$的边由父亲指向儿子,边权都为$0$。每次从$[u_1,v_1]$向$[u_2,v_2]$连边时,先新建虚点$T$,在出$S$上提取出$[u_1,v_1]$向$T$连边,在入$S$上提取出$[u_2,v_2]$并从$T$向其连边,容易看出这和暴力连边是等效的。

读入并连原生树边的同时的维护并查集筛掉不合法的操作。暂时先存起合法的1操作。

因为这是个森林,我们挨个尝试跑dfs。

设$f(u,l)$表示点$u$的$2^l$级祖先,$fi(u,l)$和$fo(u,l)$表示 点$u$一直到它的$2^l-1$级祖先构成的长度为$2^l$链 分别在入结构和出结构上的代表节点 对应的图上的点 的编号。那么在入结构里,我们有边$fi(u,l)\to fi(u,l-1)$和边$fi(u,l)\to fi(f(u,l-1),l-1)$。在出结构里有两条反向的边。特别地,$fi(u,0)=fo(u,0)=u$。

大概是这么个结构(虚线表示连着相同左端点的其他“结构点”):

连好了“结构边”,接下来连“额外边”。

对于树上的路径,先从LCA处切成两条链,然后利用类似ST表的方法连边。设点$u$的$k$级祖先函数$anc(u,k)$,对于长度为$l$的链$(u,fa)$且有$2^t\le l< 2^{t+1}$,选出$fi(u,t)$和$fi(anc(u,l-2^t),t)$即可。

接着跑堆优化Dijkstra求单源最短路。

图中点的个数:$n$个原生树点、$2n\log n$个结构点和$m$个额外点。

图中有向边的个数:$4n\log n$条结构边和$8m$条额外边。

 

代码(100分):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define _1 first
#define _2 second
using namespace std;
const int N=50000;
const int M=1e6;
const int L=15;
const int V=N+2*N*L+M;
const int E=4*N*L+8*M;
const int inf=1e9;

    int n,m,S;
    
struct Ufs{
    int f[N+3],sz[N+3];
    
    IL void init(){
        for(int i=1;i<=n;i++){
            f[i]=i;    sz[i]=1;
        }
    }
    
    int find(int x){
        return f[x]==x?x:f[x]=find(f[x]);
    }
    
    IL void merge(int x,int y){
        x=find(x);    y=find(y);
        if(sz[x]<sz[y])    swap(x,y);
        f[y]=x;    sz[x]+=(int)(sz[x]==sz[y]);
    }
    
    IL bool qry(int x,int y){
        return find(x)==find(y);
    }
    
}ufs;

struct Edge{
    int to,nxt,cap;
}e[E+3];
    int top,img,h[V+3];
    
IL void gra_init(){
    top=-1;    img=n;
    memset(h,-1,sizeof h);
    
}
    
IL void link(int u,int v,int w){
    top++;
    e[top].to=v;
    e[top].nxt=h[u];
    e[top].cap=w;
    h[u]=top;
    
}

struct Opt{
    int u1,v1,u2,v2,w;
    Opt(){}
    Opt(int u1,int v1,int u2,int v2,int w)
        :u1(u1),v1(v1),u2(u2),v2(v2),w(w){}
        
}a[M+3];
    int mm;
    
    int f[N+3][L+3],dep[N+3];
    int fi[N+3][L+3],fo[N+3][L+3];
    
void dfs(int u,int fr){
    for(int l=1;(1<<l)<=dep[u];l++){
        f[u][l]=f[f[u][l-1]][l-1];
        fi[u][l]=++img;
        fo[u][l]=++img;
        
        link(fi[u][l],fi[u][l-1],0);
        link(fi[u][l],fi[f[u][l-1]][l-1],0);
        link(fo[u][l-1],fo[u][l],0);
        link(fo[f[u][l-1]][l-1],fo[u][l],0);
        
    }
    
    for(int i=h[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fr||v>n)
            continue;
        
        f[v][0]=u;
        dep[v]=dep[u]+1;
        fi[v][0]=fo[v][0]=v;
        dfs(v,u);
        
    }
    
}

    int lg2[N+3];
    
IL void lca_init(){
    lg2[1]=0;
    for(int i=2;i<=n;i++)
    if(i==(1<<(lg2[i-1]+1)))
        lg2[i]=lg2[i-1]+1;
    else 
        lg2[i]=lg2[i-1];
    
}

IL int anc(int u,int k){
    for(int l=lg2[dep[u]];l>=0;l--)
    if((1<<l)<=k){
        u=f[u][l];    k-=1<<l;
    }
    return u;
}

IL int lca(int x,int y){
    if(dep[x]>dep[y])
        swap(x,y);
    
    y=anc(y,dep[y]-dep[x]);
    if(x==y)
        return x;
    
    for(int l=lg2[dep[x]];l>=0;l--)
    if(f[x][l]!=f[y][l]){
        x=f[x][l];    y=f[y][l];
    }
    return f[x][0];
    
}

IL void link(int u,int fa,int x,int w,int t){
    int l=dep[u]-dep[fa]+1,k=lg2[l];
    int v=anc(u,l-(1<<k));
    if(t==1){
        link(fo[u][k],x,w);    link(fo[v][k],x,w);
    }
    else {
        link(x,fi[u][k],w);    link(x,fi[v][k],w);
    }
    
}
    
struct Dat{
    int v,d;    Dat(){}
    Dat(int v,int d):v(v),d(d){}
};

IL bool operator<(Dat x,Dat y){
    return x.v>y.v;
}

    int dis[V+3];
    priority_queue<Dat>hp;
    
IL void dij(int S){
    for(int i=1;i<=img;i++)    dis[i]=inf;
    hp.push(Dat(dis[S]=0,S));
    
    while(!hp.empty()){
        Dat x=hp.top();    hp.pop();
        if(dis[x.d]<x.v)    continue;
        
        int u=x.d;
        for(int i=h[u];~i;i=e[i].nxt){
            int v=e[i].to;
            if(dis[u]+e[i].cap<dis[v])
                hp.push(Dat(dis[v]=dis[u]+e[i].cap,v));
            
        }
        
    }
    
}

int main(){
    scanf("%d%d%d",&n,&m,&S);
    ufs.init();
    gra_init();
    mm=0;
    for(int i=1;i<=m;i++){
        int opt;    scanf("%d",&opt);
        if(opt==1){
            int u1,v1,u2,v2,w;
            scanf("%d%d%d%d%d",&u1,&v1,&u2,&v2,&w);
            
            if(ufs.qry(u1,v1)&&ufs.qry(u2,v2))
                a[++mm]=Opt(u1,v1,u2,v2,w);
            
        }
        else {
            int u,v,w;    scanf("%d%d%d",&u,&v,&w);
            
            if(ufs.qry(u,v))
                continue;
            ufs.merge(u,v);
            link(u,v,w);
            link(v,u,w);
            
        }
        
    }
    
    memset(dep,0,sizeof dep);
    for(int i=1;i<=n;i++)
    if(!dep[i]){
        f[i][0]=0;
        dep[i]=1;
        fi[i][0]=fo[i][0]=i;
        dfs(i,0);
        
    }
    
    lca_init();
    for(int i=1;i<=mm;i++){
        int ans;    img++;
        ans=lca(a[i].u1,a[i].v1);
        link(a[i].u1,ans,img,0,1);
        link(a[i].v1,ans,img,0,1);
        ans=lca(a[i].u2,a[i].v2);
        link(a[i].u2,ans,img,a[i].w,2);
        link(a[i].v2,ans,img,a[i].w,2);
        
    }
    
    dij(S);
    for(int i=1;i<=n;i++)
    if(dis[i]==inf)
        printf("-1 ");
    else 
        printf("%d ",dis[i]);

    return 0;

}
View Code

 

posted @ 2020-05-21 19:52  汉谡  阅读(178)  评论(0编辑  收藏  举报