题解 P6779【[Ynoi2009] rla1rmdq】

$\text{Link}$

题意

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

  1. 令区间 $[l,r]$ 中所有 $a_i$ 变为 $a_i$ 的父亲,即 $\forall i\in[l,r],a_i\gets fa(a_i)$;
  2. 查询区间 $[l,r]$ 中 $a_i$ 在树上的带权深度的最小值,即查询 $\min_{l\le i\le r}dep(a_i)$。

时间 3s,空间 64MB,$1\le n,m\le 2\times 10^5$,边权在 $[0,10^9]$ 中。

思路

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

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

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

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

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

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

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

取块长 $450\sim600$,总复杂度 $O(n\sqrt n)$。

非常好实现的代码:

#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 @ 2022-04-07 13:03  ffffyc  阅读(12)  评论(0)    收藏  举报  来源