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;
}
posted @ 2025-07-26 15:04  根号二isWhat  阅读(19)  评论(0)    收藏  举报