题解 P6779【[Ynoi2009] rla1rmdq】

Link

题意

给出一颗 n 个结点的有根带权树和长为 n 的序列 a,支持操作共 q 次:

  1. 令区间 [l,r] 中所有 ai 变为 ai 的父亲,即 i[l,r],aifa(ai)
  2. 查询区间 [l,r]ai 在树上的带权深度的最小值,即查询 minlirdep(ai)

时间 3s,空间 64MB1n,m2×105,边权在 [0,109] 中。

思路

区间带修区间查询,考虑分块,显然这里在线只能做到根号空间,我们直接在离线逐块处理的前提下讨论。

我们称块内的 ai 对应的结点为关键点。

询问 dep 的最小值且边权非负有着很好的性质,即若一个关键点的祖先中有关键点,则后代关键点无意义。更松地,若这个关键点所处的结点曾是其余的关键点所在的位置,则这个关键点无意义。这样所有关键点只会遍历总和 O(n) 个结点。

考虑维护目前有意义的关键点编号序列,整块修改时,扫过所有有意义的关键点,将它们向上跳一个结点即可。若它跳到的结点没被标记过,则将其标记,否则将这个关键点标为无意义的。

可以发现散块修改还会把一些被标为无意义的关键点重新标为有意义的,那么维护整块修改的次数 tag 和每个关键点向上跳的次数 vali,则对散块修改的关键点查询 tagvali+1 级祖先是否被标记过,若没有且这个关键点暂无意义,则重新将其标为有意义的。

整块询问实时统计块内答案 bas 即可,散块直接求出 tagvali 级祖先即可。

这里 k 级祖先可以直接树剖,考虑同一重链上是 O(1) 的,每条重链只会造成 O(1) 的贡献,所以每个关键点只有 O(logn) 的总贡献,不影响复杂度。

取块长 450600,总复杂度 O(nn)

非常好实现的代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=2e5+10,sq=600;
struct Edge{
    int to,nxt,w;
}s[N<<1];
int n,q,rt,head[N],cnt;
inline void add(int u,int v,int w){
    cnt++;
    s[cnt].to=v;
    s[cnt].nxt=head[u];
    s[cnt].w=w;
    head[u]=cnt;
}
int dfn[N],idfn[N],tim,fa[N];
int siz[N],son[N],Top[N],d[N];
ll dep[N],ans[N];
inline void dfs1(int rt,int f){
    fa[rt]=f,siz[rt]=1;
    for(int i=head[rt];i;i=s[i].nxt){
        int t=s[i].to;
        if(t==f) continue;
        dep[t]=dep[rt]+s[i].w;
        d[t]=d[rt]+1;
        dfs1(t,rt);
        siz[rt]+=siz[t];
        if(siz[son[rt]]<siz[t]) son[rt]=t;
    }
}
inline void dfs2(int rt,int f){
    dfn[rt]=++tim;
    idfn[tim]=rt;
    if(son[rt])
        Top[son[rt]]=Top[rt],dfs2(son[rt],rt);
    for(int i=head[rt];i;i=s[i].nxt){
        int t=s[i].to;
        if(t==son[rt]||t==f) continue;
        Top[t]=t,dfs2(t,rt);
    }
}
inline int kthF(int x,int k){
    if(k>=d[x]) return rt;
    while(k>dfn[x]-dfn[Top[x]]){
        k-=dfn[x]-dfn[Top[x]]+1;
        x=fa[Top[x]];
    }
    return idfn[dfn[x]-k];
}
int a[N],bl[N],br[N],bel[N],que[N],top,tag,blk,val[N];
struct Query{
    int opt,l,r;
}qur[N];
bool vis[N],inq[N];
inline void solve(int L,int R){
    for(int i=1;i<=n;i++)
        vis[i]=inq[i]=val[i]=que[i]=0;
    ll bas=1e18;
    tag=0,top=0;
    for(int i=L;i<=R;i++)
        if(!vis[a[i]]) vis[a[i]]=inq[i]=1,que[++top]=i,bas=min(bas,dep[a[i]]);
    for(int i=1;i<=q;i++){
        int opt=qur[i].opt,l=max(qur[i].l,L),r=min(qur[i].r,R);
        if(l>r) continue;
        if(opt==1){
            if(l==L&&r==R){
                int tmp=top;
                tag++,top=0;
                for(int i=1;i<=tmp;i++){
                    val[que[i]]++;
                    if(vis[a[que[i]]=fa[a[que[i]]]]) inq[que[i]]=0;
                    else{
                        que[++top]=que[i];
                        bas=min(bas,dep[a[que[i]]]);
                        vis[a[que[i]]]=1;
                    }
                }
            }else{
                for(int i=l;i<=r;i++){
                    if(!vis[a[i]=kthF(a[i],tag-val[i]+1)]){
                        bas=min(bas,dep[a[i]]);
                        if(!inq[i])
                            que[++top]=i,vis[a[i]]=inq[i]=1;
                    }
                    val[i]=tag;
                }
            }
        }else{
            if(l==L&&r==R) ans[i]=min(ans[i],bas);
            else{
                for(int j=l;j<=r;j++)
                    ans[i]=min(ans[i],dep[a[j]=kthF(a[j],tag-val[j])]),val[j]=tag;
            }
        }
    }
}
int main(){
//  freopen("make_data.txt","r",stdin);
    n=read(),q=read(),rt=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,w);
    }
    Top[rt]=rt;
    dfs1(rt,rt),dfs2(rt,rt);
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=q;i++){
        int opt=read(),l=read(),r=read();
        qur[i]={opt,l,r},ans[i]=1e18;
    }
    for(int i=1;i<=n;i+=sq){
        blk++;
        bl[blk]=i,br[blk]=min(i+sq-1,n);
        for(int j=bl[blk];j<=br[blk];j++)
            bel[j]=blk;
    }
    for(int i=1;i<=blk;i++)
        solve(bl[i],br[i]);
    for(int i=1;i<=q;i++)
        if(qur[i].opt==2)
            write(ans[i]),putc('\n'); 
    flush();
}

再见 qwq~

posted @   ffffyc  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
点击右上角即可分享
微信分享提示