题解:P14411 [JOISC 2015] 道路建设 / Road Development

· · 题解

形式化题意

给你 n 个点,一共 q 次操作,共计两种操作。

思路

先看一会,发现需要判断连通性,所以并查集是肯定少不了的。

画图发现,对于操作一,如果两点之间存在路径,我们并不会删除原来的边,也不会添加新边;如果不存在路径,我们也只会建一条边,两点所属的联通块就会合并,两个联通块的点就存在路径。

所以我们再往下推导,不难发现全部操作完后的图是不存在环的,那不就是吗!

再看操作,有修改和查询,就不难想到用树链剖分来维护。具体来说,操作一就是分两种情况:联通,就将路径贡献变为 0,就是区间覆盖;不联通,就新建边,边权为 1

所以整道题的思路就出来了:用并查集维护连通性,离线建出树(也不一定是树,也可能是森林),再用树链剖分维护修改和查询。

代码比较长,码风有点奇怪。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define V vector
#define FOR(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define pb push_back
const int INF=1e9+10,N=5e5+10;
struct SegTree{
    struct node{
        int l,r,w,tag;
    };
    V<node>a;
    SegTree(int _n):a(_n*4+2){}
    #define ls(x) (x<<1)
    #define rs(x) (x<<1|1)
    #define mid ((a[x].l+a[x].r)>>1)
    #define push_up(x) (a[(x)].w=a[ls((x))].w+a[rs((x))].w)
    void build(int x,int l,int r){
        a[x].l=l;a[x].r=r;a[x].tag=INF;
        if(l==r)return;
        build(ls(x),l,mid);
        build(rs(x),mid+1,r);
        push_up(x);
    }
    void addtag(int x,int w){
        a[x].tag=w;
        a[x].w=(a[x].r-a[x].l+1)*w;
    }
    void push_down(int x){
        if(a[x].tag!=INF){
            addtag(ls(x),a[x].tag);
            addtag(rs(x),a[x].tag);
            a[x].tag=INF;
        }
    }
    void update(int x,int L,int R,int w){
        if(a[x].l>=L&&a[x].r<=R){
            addtag(x,w);
            return;
        }
        push_down(x);
        if(L<=mid) update(ls(x),L,R,w);
        if(R>mid) update(rs(x),L,R,w);
        push_up(x);
    }
    int query(int x,int L,int R){
        if(a[x].l>=L&&a[x].r<=R) return a[x].w;
        push_down(x);
        if(R<=mid) return query(ls(x),L,R);
        else if(L>mid) return query(rs(x),L,R);
        else return query(ls(x),L,R)+query(rs(x),L,R);
    }
    #undef ls
    #undef rs
    #undef mid
    #undef push_up
};
struct DSU{
    V<int>fa;
    DSU(int _n):fa(_n+2){
        iota(fa.begin(),fa.end(),0);
    }
    int fin(int x){
        while(x^fa[x]) x=fa[x]=fa[fa[x]];
        return x;
    }
    bool is_same(int u,int v){
        return fin(u)==fin(v);
    }
    void merge(int u,int v){
        u=fin(u);v=fin(v);
        if(u==v) return;
        fa[u]=v;
    }
};
V<int>e[N];
int dep[N],siz[N],son[N],fa[N],top[N],id[N];
int num=0;
void dfs1(int u,int fat){
    fa[u]=fat;siz[u]=1;
    dep[u]=dep[fat]+1;
    for(auto v:e[u]){
        if(v==fat)continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(!son[u]||siz[son[u]]<siz[v]) son[u]=v;
    }
}
void dfs2(int u,int tp){
    top[u]=tp;id[u]=++num;
    if(!son[u]) return;
    dfs2(son[u],tp);
    for(auto v:e[u]){
        if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
    }
}
void update(SegTree &lls,int u,int v,int w){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        lls.update(1,id[top[u]],id[u],w);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    if(u!=v) lls.update(1,id[u]+1,id[v],w);
}
int query(SegTree &lls,int u,int v){
    int ans=0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        ans+=lls.query(1,id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    if(u!=v) ans+=lls.query(1,id[u]+1,id[v]);
    return ans;
}
struct QUE{
    int op,u,v,val,ans;
};
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n,q;cin>>n>>q;
    V<QUE>que(q+1);
    SegTree lls(n+1);lls.build(1,1,n);
    DSU bcj(n+1); 
    FOR(i,1,q){
        int &op=que[i].op,&u=que[i].u,&v=que[i].v;
        cin>>op>>u>>v;
        if(op&1){
            if(!bcj.is_same(u,v)){
                bcj.merge(u,v);
                e[u].pb(v);e[v].pb(u);
                que[i].val=1;
            }
        }else{
            if(!bcj.is_same(u,v)){
                que[i].ans=-1;
            }
        }
    }
    FOR(i,1,n){
        if(!id[i]) dfs1(i,0),dfs2(i,i);
    }
    FOR(i,1,q){
        int op=que[i].op,u=que[i].u,v=que[i].v,ans=que[i].ans,val=que[i].val;
        if(op&1) update(lls,u,v,val);
        else{
            if(ans==-1) cout<<ans<<"\n";
            else cout<<query(lls,u,v)<<"\n";
        }
    }
    return 0;
}