1 条题解

  • 0
    @ 2025-8-24 22:23:53

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar cyffff
    Not yet for the story on the last page, it's not the end.

    搬运于2025-08-24 22:23:53,当前版本为作者最后更新于2022-04-07 13:03:53,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Link\text{Link}

    题意

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

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

    时间 3s,空间 64MB1n,m2×1051\le n,m\le 2\times 10^5,边权在 [0,109][0,10^9] 中。

    思路

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

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

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

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

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

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

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

    取块长 450600450\sim600,总复杂度 O(nn)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~

    • 1

    信息

    ID
    5861
    时间
    3000ms
    内存
    64MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者