[HNOI2016] 网络 题解
[HNOI2016] 网络 题解
题意简述
给定一棵 \(n\) 个点的树,以及 \(m\) 个操作:
0 a b v:点 \(a,b\) 间连上一条权值为 \(v\) 的虚边。1 t:删除 \(t\) 时刻连的虚边。2 x:查询所有两端点在原树上路径不经过 \(x\) 的虚边的权值中,最大的一个,没有则为 \(-1\)。
分析
\(30\%\)
\(m \le 2000\),每次询问遍历所有的虚边,然后进行一系列细节多得要死的判断即可,\(O(m^2)\) 或 \(O(m^2\log_2{n})\) 皆可。
\(20\%\)
第一次事件必定是加入权值最大的虚边,且没有删除操作。
大致思路:把第一次加入的虚边的路径当成一排根,分别遍历子树,然后把加入的虚边再在一排根上做一个前缀、后缀的取最值操作,用线段树实现(还有诸多细节自己想)。
时间复杂度 \(O(m\log_2{n})\)。
\(20\%\)
树是一条链,且满足 \(i\) 与 \(i+1\) 之间有边。
直接线段树套可删的堆(set<int> 或双 priority_queue<int>),也是做一个前后缀的操作。
时间复杂度 \(O(m(\log_2{n}+\log_2{m}))\)。
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define FOR(i,a,b) for(int i(a); i<=(int)(b); ++i)
#define DOR(i,a,b) for(int i(a); i>=(int)(b); --i)
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v); ~i; y=g[i=g[i].nxt].v)
using namespace std;
constexpr int N(1e5+10),M(2e5+10),lN(17),lV(lN+1);
namespace IOEcat {
#define isD(c) ('0'<=(c)&&(c)<='9')
#define DE(...) E(#__VA_ARGS__,__VA_ARGS__)
struct Icat {
char getc() {
return getchar();
}
template<class T>void operator ()(T &x) {
static bool sign(0);
static char ch(0);
sign=0,x=0;
while(ch=getc(),!isD(ch))if(ch=='-')sign=1;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getc(),isD(ch));
if(sign)x=-x;
}
template<class T,class...Types>void operator ()(T &x,Types&...args) {
return (*this)(x),(*this)(args...);
}
} I;
struct Ocat {
void putc(char c) {
putchar(c);
}
template<class T>void operator ()(T x,const char lst='\n') {
static int top(0);
static char st[100];
if(x<0)x=-x,putc('-');
do st[++top]=(x%10)^48,x/=10;
while(x);
while(top)putc(st[top--]);
putc(lst);
}
template<class T,class...Types>void operator ()(const T x,const char lst='\n',const Types...args) {
return (*this)(x,lst),(*this)(args...);
}
} O;
struct Ecat {
template<class T>void operator ()(const char *fmt,const T x) {
cerr<<fmt<<':'<<x<<'.'<<endl;
}
template<class T,class...Types>void operator ()(const char *fmt,const T x,const Types...args) {
while(*fmt^',')cerr<<*fmt++;
return cerr<<':'<<x<<" ,",(*this)(++fmt,args...);
}
} E;
} using namespace IOEcat;
bool del[M];
int n,m,idx;
int dl[N],dr[N],dep[N];
int fa[N][lV];
vector<int> g[N];
struct Change {
int t,a,b,c;
} Cha[M];
void dfs(int u) {
dl[u]=++idx,dep[u]=dep[fa[u][0]]+1;
FOR(i,1,lN)fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v:g[u])if(v^fa[u][0])fa[v][0]=u,dfs(v);
dr[u]=idx;
}
bool rela(int u,int pa) {
return dl[pa]<=dl[u]&&dr[u]<=dr[pa];
}
int lca(int u,int v) {
if(dep[u]>dep[v])swap(u,v);
DOR(i,lN,0)if((dep[v]-dep[u])&1<<i)v=fa[v][i];
if(u==v)return u;
DOR(i,lN,0)if(fa[u][i]^fa[v][i])u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
namespace Subtask1 { /*30%*/ /*O(m^2log_2{n})*/
bool Check() {
return m<=2000;
}
int Cmain() {
dfs(1);
FOR(i,1,m) {
if(Cha[i].t==1)del[Cha[i].a]=1;
else if(Cha[i].t==2){
int ans(-1);
FOR(j,1,i-1)if(!Cha[j].t&&!del[j]&&Cha[j].a!=Cha[i].a&&Cha[j].b!=Cha[i].a&&
rela(Cha[j].a,Cha[i].a)==rela(Cha[j].b,Cha[i].a)&&lca(Cha[j].a,Cha[j].b)!=Cha[i].a)
tomax(ans,Cha[j].c);
O(ans,'\n');
}
}
return 0;
}
}
struct SEG {
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
int tr[N<<2];
multiset<int> st[N<<2];
void Build(int p=1,int l=1,int r=n) {
if(l==r)return tr[p]=-1,void();
Build(ls,l,mid),Build(rs,mid+1,r),Up(p);
}
void Up(int p) {
tr[p]=max(tr[ls],tr[rs]);
}
void Insert(int x,int d,int p=1,int l=1,int r=n) {
if(l==r)return st[p].insert(d),tr[p]=*st[p].rbegin(),void();
return (x<=mid?Insert(x,d,ls,l,mid):Insert(x,d,rs,mid+1,r)),Up(p);
}
void Delete(int x,int d,int p=1,int l=1,int r=n) {
if(l==r)return st[p].erase(st[p].find(d)),tr[p]=st[p].empty()?-1:*st[p].rbegin(),void();
return (x<=mid?Delete(x,d,ls,l,mid):Delete(x,d,rs,mid+1,r)),Up(p);
}
int Max(int L,int R,int p=1,int l=1,int r=n) {
if(L<=l&&r<=R)return tr[p];
if(R<=mid)return Max(L,R,ls,l,mid);
if(mid<L)return Max(L,R,rs,mid+1,r);
return max(Max(L,R,ls,l,mid),Max(L,R,rs,mid+1,r));
}
#undef ls
#undef rs
#undef mid
} Pre,Suf;
namespace Subtask2 { /*20%*/
constexpr int Lim(1e9);
int mx(-1),U,V,P,tot;
int rt[N],idx[N];
bool Check() {
return !Cha[1].t&&Cha[1].c==Lim;
}
void _dfs(int u,int fa) {
for(int v:g[u])if(v!=fa&&!idx[v])rt[v]=rt[u],_dfs(v,u);
}
int Cmain() {
Pre.Build(),Suf.Build();
dfs(1),U=Cha[1].a,V=Cha[1].b,P=lca(U,V),tot=dep[U]-dep[P]+dep[V]-dep[P]+1;
for(int x(U),it(1);dep[x]>=dep[P];x=fa[x][0],++it)idx[x]=it;
for(int x(V),it(tot);dep[x]>=dep[P];x=fa[x][0],--it)idx[x]=it;
FOR(i,1,n)if(idx[i])rt[i]=i,_dfs(i,0);
FOR(i,1,m) {
if(Cha[i].t==0) {
int pa(lca(Cha[i].a,Cha[i].b));
// DE(i,rt[Cha[i].a],rt[Cha[i].b],Cha[i].c);
if(rt[Cha[i].a]==rt[Cha[i].b]&&pa!=rt[Cha[i].a]&&(tomax(mx,Cha[i].c),1))continue;
int l(idx[rt[Cha[i].a]]),r(idx[rt[Cha[i].b]]);
if(l>r)swap(l,r);
Pre.Insert(r,Cha[i].c),Suf.Insert(l,Cha[i].c);
} else if(Cha[i].t==2) {
if(!idx[Cha[i].a]&&(O(Lim,'\n'),1))continue;
int ans(mx),u(idx[Cha[i].a]);
// DE("DFSL",u);
if(u>1)tomax(ans,Pre.Max(1,u-1));
if(u<tot)tomax(ans,Suf.Max(u+1,tot));
O(ans,'\n');
}
}
return 0;
}
}
namespace Subtask3 { /*20%*/
bool Check() {
FOR(i,1,n-1) {
bool flag(0);
for(int j:g[i])flag|=(j==i+1);
if(!flag)return 0;
}
return 1;
}
int Cmain() {
Pre.Build(),Suf.Build();
FOR(i,1,m) {
if(Cha[i].t==0) {
int l(Cha[i].a),r(Cha[i].b);
if(l>r)swap(l,r);
Pre.Insert(r,Cha[i].c),Suf.Insert(l,Cha[i].c);
} else if(Cha[i].t==1) {
int l(Cha[Cha[i].a].a),r(Cha[Cha[i].a].b);
if(l>r)swap(l,r);
Pre.Delete(r,Cha[Cha[i].a].c),Suf.Delete(l,Cha[Cha[i].a].c);
} else {
int ans(-1),u(Cha[i].a);
if(u>1)tomax(ans,Pre.Max(1,u-1));
if(u<n)tomax(ans,Suf.Max(u+1,n));
O(ans,'\n');
}
}
return 0;
}
}
int main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
I(n,m);
FOR(i,2,n) {
int u,v;
I(u,v),g[u].push_back(v),g[v].push_back(u);
}
FOR(i,1,m)if(I(Cha[i].t,Cha[i].a),!Cha[i].t)I(Cha[i].b,Cha[i].c);
if(Subtask1::Check())return Subtask1::Cmain();
if(Subtask2::Check())return Subtask2::Cmain();
if(Subtask3::Check())return Subtask3::Cmain();
return 0;
}
\(100\%\)
树剖+线段树套可删堆
注意:该做法略卡时空。
我们尝试把链的部分分修改一下套到树上来:发现可以重链剖分,然后每次加入操作把不在 \(a,b\) 路径上的点都加入一个权值,单次操作最多 \(O(\log_2{n})\) 个加入区间,线段树区间操作 \(O(\log_2{n}\log_2{m})\),故加入操作的时间复杂度为 \(O(\log_2^2{n}\log_2{m})\) 的。删除同理。
询问直接单点查询即可,复杂度 \(O(\log_2{n}+\log_2{m})\)。
故总时间复杂度 \(O(m\log_2^2{n}\log_2{m})\),空间复杂度 \(O(m\log_2^2{n})\)。
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define FOR(i,a,b) for(int i(a); i<=(int)(b); ++i)
#define DOR(i,a,b) for(int i(a); i>=(int)(b); --i)
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v); ~i; y=g[i=g[i].nxt].v)
using namespace std;
constexpr int N(1e5+10),M(2e5+10),lN(17),lV(lN+1);
namespace IOEcat {
#define isD(c) ('0'<=(c)&&(c)<='9')
#define DE(...) E(#__VA_ARGS__,__VA_ARGS__)
struct Icat {
char getc() {
return getchar();
}
template<class T>void operator ()(T &x) {
static bool sign(0);
static char ch(0);
sign=0,x=0;
while(ch=getc(),!isD(ch))if(ch=='-')sign=1;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getc(),isD(ch));
if(sign)x=-x;
}
template<class T,class...Types>void operator ()(T &x,Types&...args) {
return (*this)(x),(*this)(args...);
}
} I;
struct Ocat {
void putc(char c) {
putchar(c);
}
template<class T>void operator ()(T x,const char lst='\n') {
static int top(0);
static char st[100];
if(x<0)x=-x,putc('-');
do st[++top]=(x%10)^48,x/=10;
while(x);
while(top)putc(st[top--]);
putc(lst);
}
template<class T,class...Types>void operator ()(const T x,const char lst='\n',const Types...args) {
return (*this)(x,lst),(*this)(args...);
}
} O;
struct Ecat {
template<class T>void operator ()(const char *fmt,const T x) {
cerr<<fmt<<':'<<x<<'.'<<endl;
}
template<class T,class...Types>void operator ()(const char *fmt,const T x,const Types...args) {
while(*fmt^',')cerr<<*fmt++;
return cerr<<':'<<x<<" ,",(*this)(++fmt,args...);
}
} E;
} using namespace IOEcat;
int n,m,idx;
int a[M],b[M],c[M],dl[N],fa[N],dep[N],siz[N],son[N],top[N];
pair<int,int> tmp[lV<<3];
struct CFS {
int tot,h[N];
struct edge {
int v,nxt;
edge(int v=0,int nxt=0):v(v),nxt(nxt) {}
} e[N<<1];
edge &operator [](int i) {
return e[i];
}
void Init(int n) {
tot=-1,RCL(h+1,-1,int,n);
}
void att(int u,int v) {
e[++tot]=edge(v,h[u]),h[u]=tot;
}
void con(int u,int v) {
att(u,v),att(v,u);
}
} g;
struct SEG {
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
struct DHP {
priority_queue<int> S,T;
void update() {
while(!S.empty()&&!T.empty()&&S.top()==T.top())S.pop(),T.pop();
}
void push(int x) {
S.push(x);
}
void pop(int x) {
T.push(x);
}
int top() {
return update(),S.empty()?-1:S.top();
}
} tr[N<<2];
template<const bool ty>void Update(int L,int R,int d,int p=1,int l=1,int r=n) {
if(L<=l&&r<=R)return !ty?tr[p].push(d):tr[p].pop(d);
if(L<=mid)Update<ty>(L,R,d,ls,l,mid);
if(mid<R)Update<ty>(L,R,d,rs,mid+1,r);
}
int Max(int x,int p=1,int l=1,int r=n) {
if(l==r)return tr[p].top();
return max(tr[p].top(),x<=mid?Max(x,ls,l,mid):Max(x,rs,mid+1,r));
}
#undef ls
#undef rs
#undef mid
} seg;
void dfs0(int u) {
dep[u]=dep[fa[u]]+1,siz[u]=1,son[u]=0;
EDGE(g,i,u,v)if(v^fa[u])fa[v]=u,dfs0(v),siz[u]+=siz[v],son[u]=(siz[son[u]]>siz[v]?son[u]:v);
}
void dfs1(int u) {
dl[u]=++idx;
if(son[u])top[son[u]]=top[u],dfs1(son[u]);
EDGE(g,i,u,v)if(v!=fa[u]&&v!=son[u])top[v]=v,dfs1(v);
}
template<const bool ty>void Update(int u,int v,int w) {
int it(0),tot(0);
for(; top[u]^top[v]; tmp[++tot]=pair<int,int>(dl[top[v]],dl[v]),v=fa[top[v]])
if(dep[top[u]]>dep[top[v]])swap(u,v);
if(dep[u]>dep[v])swap(u,v);
tmp[++tot]=pair<int,int>(dl[u],dl[v]),sort(tmp+1,tmp+tot+1);
FOR(i,1,tot) {
if(tmp[i].first-1>it)seg.Update<ty>(it+1,tmp[i].first-1,w);
tomax(it,tmp[i].second);
}
if(it<n)seg.Update<ty>(it+1,n,w);
}
int main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
I(n,m),g.Init(n);
FOR(i,2,n) {
int u,v;
I(u,v),g.con(u,v);
}
dfs0(1),top[1]=1,dfs1(1);
FOR(i,1,m) {
int t;
switch(I(t,a[i]),t) {
case 0: {
I(b[i],c[i]),Update<0>(a[i],b[i],c[i]);
break;
}
case 1: {
Update<1>(a[a[i]],b[a[i]],c[a[i]]);
break;
}
case 2: {
O(seg.Max(dl[a[i]]),'\n');
break;
}
}
}
return 0;
}
整体二分
这个做法与上面的部分分完全无关……
我们二分答案,然后将操作按原询问序列排序,然后按序遍历,把权值大于中值 \(mid\) 的虚边尝试用树状数组或线段树实现树上差分:在端点处 \(+1\),在 LCA 以及 LCA 的父节点处 \(-1\),这样查询时查子树权值和就可以得到有几条虚边经过自己;查询时判断是不是所有边都经过自己,如果是,代表 \(mid\) 对于这个询问过大,放到左边,否则放到右边。
总时间复杂度 \(O(m\log_2{n}\log_2{m})\)。
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define FOR(i,a,b) for(int i(a); i<=(int)(b); ++i)
#define DOR(i,a,b) for(int i(a); i>=(int)(b); --i)
#define EDGE(g,i,x,y) for(int i(g.h[x]),y(g[i].v); ~i; y=g[i=g[i].nxt].v)
using namespace std;
constexpr int N(1e5+10),M(2e5+10),lN(17),lV(lN+1);
namespace IOEcat {
#define isD(c) ('0'<=(c)&&(c)<='9')
#define DE(...) E(#__VA_ARGS__,__VA_ARGS__)
struct Icat {
char getc() {
return getchar();
}
template<class T>void operator ()(T &x) {
static bool sign(0);
static char ch(0);
sign=0,x=0;
while(ch=getc(),!isD(ch))if(ch=='-')sign=1;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getc(),isD(ch));
if(sign)x=-x;
}
template<class T,class...Types>void operator ()(T &x,Types&...args) {
return (*this)(x),(*this)(args...);
}
} I;
struct Ocat {
void putc(char c) {
putchar(c);
}
template<class T>void operator ()(T x,const char lst='\n') {
static int top(0);
static char st[100];
if(x<0)x=-x,putc('-');
do st[++top]=(x%10)^48,x/=10;
while(x);
while(top)putc(st[top--]);
putc(lst);
}
template<class T,class...Types>void operator ()(const T x,const char lst='\n',const Types...args) {
return (*this)(x,lst),(*this)(args...);
}
} O;
struct Ecat {
template<class T>void operator ()(const char *fmt,const T x) {
cerr<<fmt<<':'<<x<<'.'<<endl;
}
template<class T,class...Types>void operator ()(const char *fmt,const T x,const Types...args) {
while(*fmt^',')cerr<<*fmt++;
return cerr<<':'<<x<<" ,",(*this)(++fmt,args...);
}
} E;
} using namespace IOEcat;
int n,m,idx,que,lim;
int a[M],b[M],c[M],dl[N],dr[N],fa[N],ans[N],dep[N],siz[N],son[N],top[N];
struct Change {
int i,t,op;
};
struct Query {
int x,id;
};
struct CFS {
int tot,h[N];
struct edge {
int v,nxt;
edge(int v=0,int nxt=0):v(v),nxt(nxt) {}
} e[N<<1];
edge &operator [](int i) {
return e[i];
}
void Init(int n) {
tot=-1,RCL(h+1,-1,int,n);
}
void att(int u,int v) {
e[++tot]=edge(v,h[u]),h[u]=tot;
}
void con(int u,int v) {
att(u,v),att(v,u);
}
} g;
struct BIT {
#define lowbit(i) ((i)&-(i))
int c[N];
void Plus(int x,int d) {
if(x>0)for(; x<=n; x+=lowbit(x))c[x]+=d;
}
int Sum(int x) {
int ans(0);
if(x>0)for(; x; x&=x-1)ans+=c[x];
return ans;
}
int Sum(int l,int r) {
return Sum(r)-Sum(l-1);
}
#undef lowbit
} bit;
void dfs0(int u) {
dep[u]=dep[fa[u]]+1,siz[u]=1,son[u]=0;
EDGE(g,i,u,v)if(v^fa[u])fa[v]=u,dfs0(v),siz[u]+=siz[v],son[u]=(siz[son[u]]>siz[v]?son[u]:v);
}
void dfs1(int u) {
dl[u]=++idx;
if(son[u])top[son[u]]=top[u],dfs1(son[u]);
EDGE(g,i,u,v)if(v!=fa[u]&&v!=son[u])top[v]=v,dfs1(v);
dr[u]=idx;
}
int lca(int u,int v) {
while(top[u]^top[v])dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]<dep[v]?u:v;
}
void Plus(int u,int v,int w) {
int pa(lca(u,v));
bit.Plus(dl[u],w),bit.Plus(dl[v],w),bit.Plus(dl[pa],-w),bit.Plus(dl[fa[pa]],-w);
}
#define mid ((l+r)>>1)
void GBS(int l,int r,vector<Change> &Cha,vector<Query> &Que) {
DE(l,r);
if(Que.empty())return Cha.clear();
if(l==r) {
for(const Query &q:Que)ans[q.id]=l;
return Cha.clear(),Que.clear();
}
int it(0),cnt(0);
vector<Change> Cl,Cr;
vector<Query> Ql,Qr;
for(const Query &q:Que) {
while(it<(int)Cha.size()&&Cha[it].t<q.id) {
if(c[Cha[it].i]>mid)Plus(a[Cha[it].i],b[Cha[it].i],Cha[it].op),cnt+=Cha[it].op;
++it;
}
(bit.Sum(dl[q.x],dr[q.x])==cnt?Ql:Qr).push_back(q);
}
FOR(i,0,it-1)if(c[Cha[i].i]>mid)Plus(a[Cha[i].i],b[Cha[i].i],-Cha[i].op);
for(const Change &cha:Cha)(c[cha.i]<=mid?Cl:Cr).push_back(cha);
return Cha.clear(),Que.clear(),GBS(l,mid,Cl,Ql),GBS(mid+1,r,Cr,Qr);
}
#undef mid
int main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
I(n,m),g.Init(n);
FOR(i,2,n) {
int u,v;
I(u,v),g.con(u,v);
}
dfs0(1),top[1]=1,dfs1(1);
vector<Change> Cha;
vector<Query> Que;
FOR(i,1,m) {
int t;
I(t);
if(t==0)I(a[i],b[i],c[i]),Cha.push_back({i,que,1}),tomax(lim,c[i]);
else if(t==1)I(a[i]),Cha.push_back({a[i],que,-1});
else I(a[i]),Que.push_back({a[i],++que});
}
GBS(-1,lim,Cha,Que);
FOR(i,1,que)O(ans[i],'\n');
return 0;
}

浙公网安备 33010602011771号