[HNOI2016]网络
壹、题目描述 ¶
有一个 \(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\) 点。