1 条题解

  • 0
    @ 2025-8-24 23:09:17

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar DaiRuiChen007
    白鸟白鸟 不要回头望 你要替我飞去那地方 一去那地方 那是你我共同故乡

    搬运于2025-08-24 23:09:17,当前版本为作者最后更新于2025-02-15 00:32:32,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    Problem Link

    题目大意

    给定 nn 个点的基环内向树森林,mm 条个棋子要从 uvu\to v,每个时刻可以把每个棋子移动到父亲,但同一个格子的棋子只能移动一个,求所有棋子到达的最小时间。

    数据范围:n,m2×105n,m\le 2\times 10^5

    思路分析

    首先我们每轮肯定有限移动距离远终点的点,这样可以得到一个朴素暴力。

    但是这个做法无法优化,需要更好地刻画答案,考虑每条边对答案的贡献,即对于每条边 ufauu\to fa_u,我们计算所有棋子都离开 uu 的最小时刻 wuw_u

    设所有经过这条边的棋子到 uu 的距离为 d1dkd_1\sim d_k(按离开顺序排列),考虑每个棋子离开这条边的时刻 tit_i,那么有 timax(di,ti1)+1t_i\ge \max(d_i,t_{i-1})+1

    此时 wu=maxdki+1+iw_u=\max d_{k-i+1}+i,显然 dd 升序时取到最小。

    我们能证明答案恰好能取到下界 maxwu\max w_u,观察 wuw_u 的计算,相当于对于一些在 uu 的前驱处重合的棋子,我们钦定他们可以一起运动到 uu 再考虑重合的问题。

    直观上来看这个做法是很对的,因为重合后这两个棋子已经没有本质区别,无论谁先到 uu 都是等价的。

    即所有经过这条边的棋子的贡献可以正确计算,但有一些不经过 uu 的棋子,我们还要考虑他对答案的影响。

    对于一条链 xyux\to y\to u,如果有一个棋子从 xyx\to y,那么他在 xx 处可能卡住其他到 uu 的径棋子

    根据我们的朴素贪心,这显然是不可能的,因为经过 uu 的棋子剩余距离更大,因此 xyx\to y 的棋子一定会在这些棋子离开 xx 之后再运动,所有这些棋子无影响。

    所以我们能证明最优解中,最后一个棋子离开这条边的时刻恰好就是 wuw_u

    那么考虑树的情况,把过 uu 的路径用线段树合并,维护每条路径终点的深度,push up 时左边的最大答案加上右边的终点个数,这样就能维护 wuw_u 了。

    然后处理基环树,我们只要让覆盖每条边的棋子集合正确即可。

    随便找一条边破环为链,然后把整棵基环树复制一遍接到末尾,如果过 xyx\to y 路径过环边 pfapp\to fa_{p},就拆成 xpx\to pxyx'\to y,容易证明此时覆盖每条路径的棋子集合不变。

    时间复杂度 O((n+m)logn)\mathcal O((n+m)\log n)

    代码呈现

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=4e5+5;
    struct SegmentTree {
    	int tot,ls[MAXN*20],rs[MAXN*20],mx[MAXN*20],ct[MAXN*20];
    	void psu(int p) {
    		ct[p]=ct[ls[p]]+ct[rs[p]],mx[p]=max(mx[ls[p]]+ct[rs[p]],mx[rs[p]]);
    	}
    	void ins(int u,int op,int l,int r,int &p) {
    		if(!p) p=++tot;
    		if(l==r) return ct[p]+=op,mx[p]=(ct[p]?l+ct[p]:0),void();
    		int mid=(l+r)>>1;
    		u<=mid?ins(u,op,l,mid,ls[p]):ins(u,op,mid+1,r,rs[p]);
    		psu(p);
    	}
    	void merge(int l,int r,int q,int &p) {
    		if(!q||!p) return p|=q,void();
    		if(l==r) return ct[p]+=ct[q],mx[p]=(ct[p]?l+ct[p]:0),void();
    		int mid=(l+r)>>1;
    		merge(l,mid,ls[q],ls[p]),merge(mid+1,r,rs[q],rs[p]);
    		psu(p);
    	}
    }	T;
    vector <int> G[MAXN],D[MAXN];
    int n,m,fa[MAXN],dsu[MAXN],tp[MAXN];
    int find(int x) { return x^dsu[x]?dsu[x]=find(dsu[x]):x; }
    int dcnt,dfn[MAXN],rdn[MAXN],dep[MAXN];
    void dfs1(int u) {
    	dfn[u]=++dcnt;
    	for(int v:G[u]) dep[v]=dep[u]+1,tp[v]=u?tp[u]:v,dfs1(v);
    	rdn[u]=dcnt;
    }
    int op[MAXN],ans,rt[MAXN];
    void ins(int x,int y) { ++op[y],D[x].push_back(dep[y]); }
    void dfs2(int u) {
    	for(int v:G[u]) dfs2(v),T.merge(0,2*n,rt[v],rt[u]);
    	T.ins(dep[u],op[u],0,2*n,rt[u]);
    	for(int i:D[u]) T.ins(i,-1,0,2*n,rt[u]);
    	ans=max(ans,T.mx[rt[u]]-dep[u]);
    }
    signed main() {
    	scanf("%d",&n),iota(dsu+1,dsu+n+1,1);
    	for(int i=1;i<=n;++i) scanf("%d",&fa[i]),fa[i+n]=fa[i]+n,dsu[find(i)]=find(fa[i]);
    	for(int i=1;i<=n;++i) if(dsu[i]==i) {
    		int x=i;
    		for(;dsu[x];x=fa[x]) dsu[x]=0;
    		fa[x+n]=fa[x],fa[x]=0;
    	}
    	for(int i=1;i<=2*n;++i) G[fa[i]].push_back(i);
    	dfs1(0),scanf("%d",&m);
    	for(int x,y;m--;) {
    		scanf("%d%d",&x,&y);
    		if(dfn[y]<=dfn[x]&&dfn[x]<=rdn[y]) ins(y,x);
    		else if(dfn[y]<=dfn[x+n]&&dfn[x+n]<=rdn[y]) ins(y,x+n),ins(tp[y],x);
    		else return puts("-1"),0;
    	}
    	dfs2(0),printf("%d\n",ans);
    	return 0;
    }
    
    • 1

    信息

    ID
    11430
    时间
    2000ms
    内存
    1024MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者