首页 > 其他分享 >三化二叉树trick

三化二叉树trick

时间:2022-12-30 18:34:51浏览次数:44  
标签:node cnt2 三化 trick 棋子 swap ans 2a 二叉树

三选一化二叉

套路概述

这个套路是针对某一建模题的。

三选一其实可以扩展到N选一,模型具体如下。

发现某种状态可以扩展出\(N\)个状态,且有一个状态相较而言比较特殊(如其他状态都是扩张,仅有这个是收缩)的时候,可以考虑建立起一棵树,以当前状态为节点,特殊状态为父节点,其余状态为子节点。将问题转化到树上,而三选一就是转为二叉树的基本模型。如果没有特殊状态也可以考虑建立一张图进行处理。

建模-跳跳棋

跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有三颗棋子,分别在 \(a,b,c\)这三个位置。我们要通过最少的跳动把他们的位置移动成\(x,y,z\) (注意:棋子是没有区别的)。

跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过一颗棋子。

写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。

分析

考虑一个三元组\((a,b,c)(a<b<c)\)可以怎么跳:

  1. \((2a-b,a,c)\)
  2. \((a,c,2c-b)\)
  3. \((b,2b-a,c)(|a-b|<|b-c|)\)
  4. \((a,2b-c,b)(|a-b|>|b-c|)\)

这四种操作里面,容易发现三四操作最多只能存在一种。

容易发现,操作具有可逆性。而操作1,2都导致\((a',b',c')\)边界向外扩张,而只有3,4向内收缩。

突然发现,如果\(|a-b|=|b-c|\),此时只能进行1,2操作,也即进行扩张。而操作1,2可以分为\(b\)向左/右跳的结果。由于是LCA的题,不难想到需要建模。

我们可以由一组\((a,b,2b-a)\)开始不断向外扩展,不妨扩展看看,记\(c=2b-a\)

1次:\((2a-b,a,c),(a,c,2c-b)\)
2次:\((3a-2b,2a-b,c),(2a-b,c,2c-a),(2a-c,2a-b,2c-a),(a,2c-b,3c-2b)\)

好像,它形成了一个树形结构?也即中间棋子向左跳为左儿子,向右跳为右儿子,向内跳的情况为父亲,这样就由\((a,b,c)\)能够到达的所有情况变成了一颗满二叉树。

此时,由于操作的可逆性,容易发现初始状态和终态有解当且仅当他们所在树的树根相同。然后最少操作次数就是二者在树上的距离。

但是容易发现这棵树好像无穷大,根本不可能建立出来好吧。我们先来考虑如何快速求三元组\((a,b,c)\)的根,亦或者说,我们考虑如何快速求解三元组\((a,b,c)\)的第\(k\)级祖先。

数据范围\(10^9\),显然可以卡掉所有复杂度不正确的算法。

考虑这样一个过程,我们优化掉连续向某个方向跳的操作,这个可以通过公式计算:

设\(x_1=b-a,x_2=c-b\),则若\(x_1<x_2\),则可以不断向右跳\(\left\lfloor\frac{x_2-1}{x_1}\right\rfloor\)步。

然后\(x_1'=x_2-x_1\left\lfloor\frac{x_2-1}{x_1}\right\rfloor,x_2'=x_1\)

这是一个类似于计算欧几里得算法的过程,到最终肯定会有一个终态。于是这部分复杂度被优化到了\(O(\log n)\)。

再接着,由于计算树上路径需要LCA,而这棵树的性质决定了我们不可能使用常用LCA算法优化这个过程。但倍增的思想告诉我们,我们可以自底向上地倍增!具体的,先将两个节点调整到同一高度。由于这个算法的存在,可以在\(O(\log n)\)的时间里求出某个节点的\(k\)级祖先,此时就可以食用倍增优化了。总体复杂度\(O(\log^2 n)\)。

事实上,这个复杂度应该远远跑不满才对。

#include<iostream>
#include<cstdio>
using namespace std;
struct node{
	int a,b,c;
	void init(){
		cin>>a>>b>>c;
		if(a>b)swap(a,b);if(a>c)swap(a,c);if(b>c)swap(b,c);
	}
	bool operator==(node x){
		return a==x.a&&b==x.b&&c==x.c;
	}
}a,b,fa,fb;
int cnt1,cnt2,ans,l1,l2;
void bezero(node &x){  
    if(x.a>x.b)swap(x.a,x.b);
    if(x.a>x.c)swap(x.a,x.c);
    if(x.b>x.c)swap(x.b,x.c);
}
node get(node a,int k,int &cnt){//得到a的k级父亲 
	node ans=a;
	cnt=0;
	while(k){
	   bezero(ans);
		int x1=ans.b-ans.a,x2=ans.c-ans.b,s;
		if(x1<x2){
			s=min(k,(x2-1)/x1);
			ans.a+=s*x1;
			ans.b+=s*x1;
			k-=s;
		}
		else if(x1>x2){
			s=min(k,(x1-1)/x2);
			ans.b-=s*x2;
			ans.c-=s*x2;
			k-=s;
		}
		else {
			return ans;
		}
		cnt+=s;
	}
	return ans;
}
int main(){
	a.init();b.init();
	fa=get(a,0x3f3f3f3f,cnt1);
	fb=get(b,0x3f3f3f3f,cnt2);
	if(!(fa==fb)){
		cout<<"NO\n";
		return 0;
	}
	cout<<"YES\n";
	if(cnt1>cnt2)swap(a,b),swap(cnt1,cnt2);
	ans+=cnt2-cnt1;
	b=get(b,cnt2-cnt1,cnt2);
	if(a==b){
	    cout<<ans;
	    return 0;
	}
	for(int i=29;i>=0;--i){
		node x=get(a,1<<i,l1),y=get(b,1<<i,l2);
		if(!(x==y)){
			a=x,b=y;
			ans+=1<<(i+1);
		}
	}
	cout<<ans+2;
}

清华集训的题都是神题

标签:node,cnt2,三化,trick,棋子,swap,ans,2a,二叉树
From: https://www.cnblogs.com/oierpyt/p/17015612.html

相关文章

  • leetcode-543. 二叉树的直径
    543.二叉树的直径-力扣(Leetcode)深度优先遍历,每个节点的直径等于左子树的最大深度加上右子树的最大深度,取一个最大值即可/***Definitionforabinarytreenode.......
  • 四、数据结构第四节——二叉树(知识点)
    四、数据结构第四节——二叉树今天开启美妙的二叉树的学习~~~“树”是我们第一次见到的”非线性”的数据结构。二叉树:是树上每个节点都只有两个子节点的简单的树。知......
  • 二叉树
    二叉树的概念树,有三个比较相似的概念:高度,深度,层;它们的定义为:节点的高度:节点到叶子节点的最长路径节点的深度:根节点到这个节点所经历的边的个数节点的层数:节点的深度+......
  • 关于二叉树深度 和 高度的问题
    根节点的高度=最大深度(后序遍历)104.二叉树的最大深度publicintmaxDepth(TreeNoderoot){returngetDepth(root);}publicintgetDepth(TreeNo......
  • 二叉树
    二叉树的定义二叉树是一种有序树,它是节点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。它的特点是每个节......
  • #yyds干货盘点# 名企真题专题:将满二叉树转换为求和树
    1.简述:描述给出满二叉树的前序遍历结果和中序遍历结果,编写算法将其转化为求和树什么是求和树:二叉树的求和树,是一颗同样结构的二叉树,其树中的每个节点将包含原始树中的左子......
  • 二叉树
    226. 翻转二叉树给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。解题思路:对于每一个节点来说,先将其左右子节点交换,然后分别递归处理左右节点funci......
  • 代码随想录算法训练营Day21|530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、
    代码随想录算法训练营Day21|530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236.二叉树的最近公共祖先530.二叉搜索树的最小绝对差530.二叉搜索树的最小绝对......
  • 代码随想录算法训练营Day20|654. 最大二叉树、700. 二叉搜索树中的搜索、98. 验证二叉
    代码随想录算法训练营Day20|654.最大二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树654.最大二叉树654.最大二叉树注意题干信息:整数数组nums元素不重复:元......
  • 带重复节点的前序中序二叉树
    已知一个二叉树的先序遍历序列和中序遍历序列,但其中一些节点的值可能相同。请你返回所有满足条件的二叉树。二叉树在数组中的顺序是任意的。输入[1,1,2],[1,2,1]输出[......