• Atcoder Grand Contest 005 E Sugigma: The Showdown(思维题)


    洛谷题面传送门 & Atcoder 题面传送门

    记先手移动棋子的树为红树,后手移动棋子的树为蓝树。

    首先考虑一个性质,就是如果与当前红色棋子所在的点相连的边中存在一条边,满足这条边的两个端点在蓝树上的距离 \(\ge 3\),那么答案肯定是 \(-1\),因为如果此时红色棋子与蓝色棋子在蓝树上的距离 \(\le 1\),那么先手就可以沿着这条红边将红色棋子移到该边的另一个端点,使红色棋子与蓝色棋子在蓝树上的距离 \(>1\),否则原地不动。如果后手再次将蓝色棋子移到与红色棋子在蓝树上距离为 \(1\) 的位置那先手可以再将棋子沿着这条红边移动 \(1\) 的距离,如此反复进行下去即可。

    因此我们考虑先对蓝树进行一遍 DFS 并对每一条红边检验其在蓝树上的距离是否 \(\ge 3\),记 \(ok_x\) 表示是否存在一条与 \(x\) 相连的红边满足其在蓝树上的距离 \(\ge 3\),那么如果先手能够走到一个满足 \(ok_x=1\) 的点,并且下一步还是先手走,那么答案就是 \(-1\)

    那么怎么处理答案不等于 \(-1\) 的情况呢?别急,我们再来找些性质,我们不妨假设现在还不能到达能够无限循环的状态,也就是说所有与当前红色棋子所在点相连的边在蓝树上的距离 \(\le 2\),那么有一个性质是红色棋子肯定不会跨过蓝色棋子,因为如果红色棋子跨过了蓝色棋子,那么此时红色棋子与蓝色棋子距离必然 \(\le 1\),后手必然能在一步之内结束游戏,这显然是先手所不希望看到的。换句话说,任意时刻红色棋子必定在蓝色棋子在蓝树上的子树内,证明可以归纳,读者自证不难。有了这个性质之后我们还能顺带着推出一个性质,那就是记 \(depr_i\)\(i\) 到红树树根的距离,\(depb_i\)\(i\) 到蓝树树根的距离,那么任意时刻如果游戏没有结束,都有 \(depr_x<depb_x\),否则此时此刻红色棋子不在蓝色棋子在蓝树上的子树内,或者两个点位于同一节点上,游戏结束。因此考虑再对红树进行一遍 DFS,如果先手经过 \(depr_x<depb_x\) 的点能够走到 \(ok_x=1\) 的点那么答案就是 \(-1\),否则我们找出红色棋子经过的点中 \(depb\) 的最大值 \(mx\),那么容易证明最优方案是先手将红色棋子移到这个 \(depb\) 最大的点然后原地不动等死,经过的步数为 \(2mx\)

    时间复杂度线性,判断两点在蓝树上距离是否 \(\ge 3\) 不必倍增跳 LCA 求距离,可以直接求出 \(x,y\)\(0,1,2\) 级祖先并判断是否有相同的值。

    终于在 NOI 之前把鸽掉的题解全部补完了

    const int MAXN=2e5;
    int n,x,y;vector<pii> edges_r;
    struct graph{
    	int hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
    	void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
    } R,B;
    int dep_b[MAXN+5],fa_b[MAXN+5],dep_r[MAXN+5];
    void dfs1(int x,int f){
    	fa_b[x]=f;
    	for(int e=B.hd[x];e;e=B.nxt[e]){
    		int y=B.to[e];if(y==f) continue;
    		dep_b[y]=dep_b[x]+1;dfs1(y,x);
    	}
    }
    bool check(int x,int y){
    	vector<int> fx,fy;
    	for(int i=0;i<3;i++) fx.pb(x),x=fa_b[x];
    	for(int i=0;i<3;i++) fy.pb(y),y=fa_b[y];
    	for(int i=0;i<3;i++) for(int j=0;j+i<3;j++) if(fx[i]==fy[j]) return 0;
    	return 1;
    }
    int ans=0,ok[MAXN+5];
    void dfs2(int x,int f){
    	if(dep_r[x]>=dep_b[x]) return;
    	if(ok[x]) puts("-1"),exit(0);
    	chkmax(ans,dep_b[x]);
    	for(int e=R.hd[x];e;e=R.nxt[e]){
    		int y=R.to[e];if(y==f) continue;
    		dep_r[y]=dep_r[x]+1;dfs2(y,x);
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&x,&y);
    	for(int i=1,u,v;i<n;i++){
    		scanf("%d%d",&u,&v);edges_r.pb(mp(u,v));
    		R.adde(u,v);R.adde(v,u);
    	}
    	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),B.adde(u,v),B.adde(v,u);
    	dfs1(y,0);for(pii e:edges_r) if(check(e.fi,e.se)) ok[e.fi]=ok[e.se]=1;
    	dfs2(x,0);printf("%d\n",ans<<1);
    	return 0;
    }
    
  • 相关阅读:
    宏队列与微队列
    async 与 await
    promise关键点
    promiseAPI
    promise基本使用
    JS中的错误(Error)即错误处理
    两种类型的回调函数(同步回调与异步回调)
    区别实例对象与函数对象
    在二叉树中查找指定值结点的所有祖先
    关于js点击事件出现 xx is not defined at HTMLAnchorElement.onclick 的问题
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc005E.html
Copyright © 2020-2023  润新知