[HNOI2016]网络

壹、题目描述 ¶

传送门 to Luogu.

有一个 \(n\) 个点的树,有三种操作:

  • 加入一条路径 \(\lang u,v,w\rang\),其中 \(w\) 为路径的价值;
  • 删除一条路径 \(\lang u,v,w\rang\)
  • 询问没有经过点 \(u\) 的路径中的最大价值;

贰、题解 ¶

对于询问,我们可以使用二分,得到 \(x\) 之后,检查价值大于等于 \(x\) 的路径的交集是否有 \(u\),而对于路径交集,我们可以维护一棵权值线段树(当然需要先离散化,否则会出现重复价值的路径)。复杂度 \(\mathcal O(n\log^2 n)\),但是我们本来就有权值线段树了,我们可以做线段树二分,复杂度变为 \(\mathcal O(n\log n)\),当然,你需要使用 \(\rm st\) 表处理 \(\rm lca\).

似乎十分简单,但有几个小问题:

  • 如何判断 \(p\) 在路径 \(\lang u,v\rang\) 上?使用 \(\text{dis}(u,v)\)\(\text{dis}(u,p)+\text{dis}(p,v)\) 来判断;
  • 如何求路径的交?先将两条路径上的四个端点,两两 \(\rm lca\) 求出来(同一路径上的不用),一共有四个,选择深度最大的俩,如果不是同一个点,那这俩点就是端点,如果是同一个点,还需要判断是否是在端点相交,如果都不是,那么交集为空;

然后就解决了。

叁、参考代码 ¶

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
#include<iostream>
#include<queue>
using namespace std;

// #define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    // #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int maxn=1e5;
const int maxm=2e5;
const int logn=17;

int n, m;
struct query{
    int t, x; pair<pii, int>p;
}q[maxm+5];
pair<pii, int>road[maxm+5];
int rcnt;

vector<int>g[maxn+5];
inline void add_edge(int u, int v){
    g[u].push_back(v); g[v].push_back(u);
}

inline void input(){
    n=readin(1), m=readin(1);
    int u, v;
    rep(i, 1, n-1){
        u=readin(1), v=readin(1);
        add_edge(u, v);
    }
    rep(i, 1, m){
        q[i].t=readin(1);
        if(q[i].t==0){
            q[i].p.fi.fi=readin(1), q[i].p.fi.se=readin(1), q[i].p.se=readin(1);
            road[++rcnt]=q[i].p;
        }
        else q[i].x=readin(1);
    }
}

inline bool cmp(pair<pii, int> x, pair<pii, int> y){ return x.se<y.se; }
inline void hashroad(){
    sort(road+1, road+rcnt+1, cmp);
    rep(i, 1, m) if(q[i].t<=1){
        if(q[i].t==0) q[i].x=lower_bound(road+1, road+rcnt+1, q[i].p, cmp)-road;
        else q[i].x=q[q[i].x].x;
    }
}

int dep[maxn+5], dfn[maxn+5], timer;
int st[logn+5][maxn*2+5], lgg[maxn*2+5];
void dfs(int u, int par){
    dep[u]=dep[par]+1, dfn[u]=++timer;
    st[0][dfn[u]]=u;
    for(int v: g[u]) if(v!=par){
        dfs(v, u);
        st[0][++timer]=u;
    }
}

inline void buildst(){
    lgg[0]=-1;
    for(int i=1; i<=timer; ++i) lgg[i]=lgg[i>>1]+1;
    rep(i, 1, logn) rep(j, 1, timer-(1<<i-1))
        st[i][j]=(dep[st[i-1][j]]<dep[st[i-1][j+(1<<i-1)]]? st[i-1][j]: st[i-1][j+(1<<i-1)]);
}

inline int lca(int u, int v){
    int l=dfn[u], r=dfn[v], tmp;
    if(l>r) swap(l, r);
    tmp=lgg[r-l+1];
    return dep[st[tmp][l]]<dep[st[tmp][r-(1<<tmp)+1]]? st[tmp][l]: st[tmp][r-(1<<tmp)+1];
}


inline int dis(int u, int v){
    int tp=lca(u, v);
    return dep[u]+dep[v]-(dep[tp]<<1);
}
inline bool contain(int x, pii path){
    if(path.fi==0) return 1;
    else if(path.fi==-1) return 0;
    if(dis(path.fi, x)+dis(x, path.se)==dis(path.fi, path.se))
        return 1;
    return 0;
}
namespace saya{
    pii tre[maxm<<2|2];
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define mid ((l+r)>>1)
    #define _lq ls, l, mid
    #define _rq rs, mid+1, r
    inline int merge_cmp(int u, int v){
        return dep[u]>dep[v];
    }
    inline pii merge(pii x, pii y){
        // empty set
        if(x.fi==-1 || y.fi==-1) return mp(-1, -1);
        // one of the two sets contains nothing
        if(x.fi==0) return y;
        if(y.fi==0) return x;
        static int p[4];
        p[0]=lca(x.fi, y.fi), p[1]=lca(x.fi, y.se);
        p[2]=lca(x.se, y.fi), p[3]=lca(x.se, y.se);
        sort(p, p+4, merge_cmp);
        if(p[0]==p[1] && p[0]!=lca(x.fi, x.se) && p[0]!=lca(y.fi, y.se)) return mp(-1, -1);
        return mp(p[0], p[1]);
    }
    inline void pushup(int i){
        tre[i]=merge(tre[ls], tre[rs]);
    }
    void insert(int i, int l, int r, int p, pii x){
        if(l==r) return tre[i]=x, void();
        if(p<=mid) insert(_lq, p, x);
        else insert(_rq, p, x);
        pushup(i);
    }
    int query(int i, int l, int r, int x){
        if(contain(x, tre[i])) return -1;
        if(l==r) return l;
        if(contain(x, tre[rs])) return query(_lq, x);
        else return query(_rq, x);
    }
    #undef ls
    #undef rs
    #undef mid
    #undef _lq
    #undef _rq
}

inline int bisearch(int x){
    return saya::query(1, 1, rcnt, x);
}

inline void solve(){
    rep(i, 1, m){
        if(q[i].t==0) saya::insert(1, 1, rcnt, q[i].x, q[i].p.fi);
        else if(q[i].t==1) saya::insert(1, 1, rcnt, q[i].x, mp(0, 0));
        else if(q[i].t==2){
            int ret=bisearch(q[i].x);
            if(ret==-1) printf("-1\n");
            else writc(road[ret].se);
        }
    }
}

signed main(){
    input();
    hashroad();
    dfs(1, 0);
    buildst();
    solve();
    return 0;
}

肆、关键之处 ¶

在一堆路径中,存在没有经过 \(u\) 的路径 \(\rightarrow\) 这堆路径的交没有 \(u\) 点。

posted @ 2021-07-15 22:43  Arextre  阅读(36)  评论(0编辑  收藏  举报