P7735 [NOI2021] 轻重边 题解
小蒟蒻发的第一篇题解,恳请包容awa
题目传送门
题面描述
给出一棵 \(n\) 个节点的树。树上的边有轻重之分,开始时,所有边均为轻边。
有 \(m\) 次操作,每次操作给定 \(op,a,b\):
- \(op = 1\),将 \(a,b\) 最短路径上的边赋为重边,与 \(a,b\) 最短路径上的点相连的其他边赋为轻边。
- \(op=2\),求 \(a,b\) 最短路径上重边的个数。
保证 \(a \neq b\)。
数据范围: \(n,m \leq 10^{5}\)。
时间限制: \(1000ms\)
分析
我们考虑转换一下问题,在修改操作时,将路径上的点全部染成一种独一无二的颜色,判断重边只需要判断两端点颜色是否相同即可。
为什么呢?每次染色时,颜色相同,即可保证路径上所有的边两端点同色,即为重边;又因为这个颜色是独一无二的,因此与这条路径连接的其他边,两端点颜色必然不同,即为轻边。
于是,问题就变成了:如何统计树的一段路径上同色相邻点对的数量?
先考虑链的形式:明显可以用 线段树 去维护
对于每个线段树的节点,维护三个量:左端点颜色、右端点颜色以及同色相邻点对的数量,合并时就可以用两区间接头处颜色来判断。修改操作就是修改区间权值。
有了链上的做法,套到树上也很简单,用 树剖 去维护。
修改时,直接跳链修改权值;查询时,因为跳链过程中会有很多单独的点(链头等于自身),直接用线段树查询为0,因此需要判断链头与它的父亲颜色是否相等,若相等则数量+1,避免漏。
时间复杂度 \(O(n \log^2{n} )\)
代码:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>
#define maxn 100005
#define ls(u) ((u)<<1)
#define rs(u) (((u)<<1)|1)
#define mid(l,r) ((l+r)>>1)
using namespace std;
/*----------vars----------*/
int T,n,q,op,a,b;
int head[maxn],tot=0;
struct node{int next,to;} edge[maxn*2];
struct tree{int col,lft,rht;} w[maxn*4];
int siz[maxn],son[maxn],fa[maxn],deep[maxn];
int lz[maxn*4],id[maxn],top[maxn],cnt;
/*----------func_init----------*/
void init(){
//有可能没必要,但保险起见
memset(head,0,sizeof(head));
memset(edge,0,sizeof(edge));
memset(deep,0,sizeof(deep));
memset(top,0,sizeof(top));
memset(son,0,sizeof(son));
memset(siz,0,sizeof(siz));
memset(lz,0,sizeof(lz));
memset(id,0,sizeof(id));
memset(fa,0,sizeof(fa));
memset(w,0,sizeof(w));
tot=0,cnt=0;
}
void addedge(int u,int v){
edge[++tot].to=v;
edge[tot].next=head[u];
head[u]=tot;
}
int read(){
int x=0,f=1,ch=getchar();
while(!isdigit(ch)) f=(ch=='-'?-1:1),ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return x*f;
}
/*----------func_SegTree----------*/
bool mai(int l,int r,int L,int R) {return (l>=L)&&(r<=R);}
void pushup(int u){
w[u].col=w[ls(u)].col+w[rs(u)].col+(w[ls(u)].rht==w[rs(u)].lft); //判断两区间接头处颜色
w[u].lft=w[ls(u)].lft;
w[u].rht=w[rs(u)].rht;
}
void build(int u,int l,int r){
if(l==r){
w[u].col=0,w[u].lft=l+q,w[u].rht=l+q; //将每个颜色设成不同的,保证初始化轻边
return;
}
int m=mid(l,r);
build(ls(u),l,m),build(rs(u),m+1,r);
pushup(u);
}
void mtag(int u,int len,int x){
lz[u]=x;
w[u].lft=x,w[u].rht=x,w[u].col=len-1;
}
void pushdown(int u,int l,int r){
int m=mid(l,r);
if(lz[u]==0) return;
mtag(ls(u),m-l+1,lz[u]);
mtag(rs(u),r-m,lz[u]);
lz[u]=0;
}
void update(int u,int l,int r,int L,int R,int x){
if(mai(l,r,L,R)) {mtag(u,r-l+1,x); return;}
pushdown(u,l,r);
int m=mid(l,r);
if(L<=m) update(ls(u),l,m,L,R,x);
if(R>m) update(rs(u),m+1,r,L,R,x);
pushup(u);
}
int query(int u,int l,int r,int L,int R){
if(mai(l,r,L,R)) return w[u].col;
pushdown(u,l,r);
int m=mid(l,r),ans=0;
if(L<=m) ans+=query(ls(u),l,m,L,R);
if(R>m) ans+=query(rs(u),m+1,r,L,R);
if((L<=m&&R>m)&&w[ls(u)].rht==w[rs(u)].lft) ans++;
return ans;
}
int point(int u,int l,int r,int pos){ //颜色单点查询
if(l==r) return w[u].lft;
pushdown(u,l,r);
int m=mid(l,r);
if(pos<=m) return point(ls(u),l,m,pos);
else return point(rs(u),m+1,r,pos);
}
/*----------func_Qtree----------*/
void dfs1(int u,int f){
int mson=0;
siz[u]=1;
for(int c=head[u];c;c=edge[c].next){
int v=edge[c].to;
if(v==f) continue;
fa[v]=u,deep[v]=deep[u]+1;
dfs1(v,u);
siz[u]+=siz[v];
if(mson<siz[v]) mson=siz[v],son[u]=v;
}
}
void dfs2(int u,int tp){
id[u]=++cnt;
top[u]=tp;
if(son[u]==0) return;
dfs2(son[u],tp);
for(int c=head[u];c;c=edge[c].next){
int v=edge[c].to;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
void updateLen(int u,int v,int x){
while(top[u]!=top[v]){
if(deep[top[u]]<deep[top[v]]) swap(u,v);
update(1,1,n,id[top[u]],id[u],x);
u=fa[top[u]];
}
if(deep[u]>deep[v]) swap(u,v);
update(1,1,n,id[u],id[v],x);
}
int queryLen(int u,int v){
int ans=0;
while(top[u]!=top[v]){
if(deep[top[u]]<deep[top[v]]) swap(u,v);
ans+=query(1,1,n,id[top[u]],id[u]);
if(point(1,1,n,id[fa[top[u]]])==point(1,1,n,id[top[u]])) ans++; //!!!
u=fa[top[u]];
}
if(deep[u]>deep[v]) swap(u,v);
ans+=query(1,1,n,id[u],id[v]);
return ans;
}
/*----------main----------*/
int main(){
T=read();
while(T--){
init();
n=read(),q=read();
for(int i=1;i<n;i++){
a=read(),b=read();
addedge(a,b),addedge(b,a);
}
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
for(int i=1;i<=q;i++){
op=read(),a=read(),b=read();
if(op==1) updateLen(a,b,i);
if(op==2) printf("%d\n",queryLen(a,b));
}
}
return 0;
}

浙公网安备 33010602011771号