6368. 【NOIP2019模拟2019.9.25】质树
题目
题目大意
有个二叉树,满足每个点跟它的所有祖先互质。
给出二叉树的中序遍历的点权,还原一种可能的方案。
思考历程
首先想到的当然是找到一个跟全部互质的点作为根,然后左右两边递归下去处理……
然而考虑到和全部互质的点可能有很多个,这样的做法可能会退化到很多……
先预处理了个\(L_i\)和\(R_i\)表示\(i\)左边第一个和\(i\)不互质的位置和右边第一个和\(i\)不互质的点。
这个东西怎么预处理就不用说吧……
(我估计正解肯定也要处理这东西)
然后就是乱搞……
想不出正解,于是打了个\(O(n^3)\)的区间\(DP\)……
在时间复杂度上似乎连一分都没有……
正解
在比赛的后期我发现了一个很有用的结论:
如果一个区间可以成为一棵合法的子树,那么这个区间的所有子区间都可以成为一棵合法的子树。
这个是比较显然的,原因是什么自己脑补……我还拍过了几个数据……
(然而我在比赛的最后都不知道该怎么用……)
然后YMQ告诉我一个性质:如果一个区间中选择任意一个跟其它所有数互质的点,然后递归下去处理,处理完之后发现不行,那么选择其它的和所有数互质的点处理也不行。
回去一个晚上想到了证明:
上面的那个结论反过来就是说,如果一个区间不能成为一棵合法的子树,那么所有包含它的区间都不能成为一棵合法的子树。
假设这个区间是\([l,r]\),其中的一个和其它所有数互质的点为\(x\)。
如果处理之后不行,则必然是\([l,x-1]\)或\([x+1,r]\)不行。
所以包含\([l,x-1]\)或\([x+1,r]\)的所有区间都不行啊……这区间也自然包括\([l,r]\)
接下来就有一种比较简单又自然的做法:随便找一个和其它数互质的点,作为子树的根,左右递归下去。显然时间复杂度是\(O(n^2)\)的。
回去睡了一个晚上,想到了\(O(n\lg n)\)的做法:从左右两边同时寻找,找到之后就递归。这样看上去似乎没有什么优化,实际上,相当于寻找的时间复杂度小于等于当前区间长度的一半,然后继续递归。
可以看做这个小区间递归了,大区间继续找,那最后就是大区间在区间长度的时间复杂度内,分成了若干个长度小于等于总长的一半的小区间递归下去。
所以就只有\(O(\lg n)\)层。
然后我发现这就是题解做法之一……另一种做法比较强大,在预处理之后就是\(O(n)\)的(我最后就是打了这种做法)。
用一个栈来维护最右边的一条链,栈顶就是深度最大的那个点。
设栈顶为\(b\),栈顶下面的那一个为\(a\)。
考虑增量法,每次新增一个\(i\)进去。
然后看看能不能通过旋转使得答案更优。
能够旋转的必要条件是\(L_i< a+1\)(因为\(a+1\)是先前\(b\)子树区间的左端点)
使得更优的必要条件是\(R_i\geq R_b\),这就意味着右儿子可以添加更多的点,当然更优。
如果可以成功旋转就一直试着旋转下去,直到变成根或者不优。
搞完之后,判断一下是否\(i<R_b\)。如果不是,那就impossible。
按照我的理解,由于前面的策略已经保证了向右延伸得最多,所以如果依然不能容纳\(i\),那么证明肯定不合法。
这样一直建下去就行了。
代码
由于oj出锅了,所以我也不知道这个代码对不对……
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
//#include <cmath>
#define N 1000010
#define maxA 10000000
inline int input(){
char ch=getchar();
while (ch<'0' || ch>'9')
ch=getchar();
int x=0;
do{
x=x*10+ch-'0';
ch=getchar();
}
while ('0'<=ch && ch<='9');
return x;
}
int n,a[N];
int p[maxA],np;
int mnp[maxA+10];
int lst[maxA+10];
int L[N],R[N];
int st[N],top;
int fa[N],son[N][2];
int main(){
// freopen("in.txt","r",stdin);
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
for (int i=2;i<=maxA;++i){
if (!mnp[i]){
p[++np]=i;
mnp[i]=i;
}
for (int j=1;j<=np && i*p[j]<=maxA;++j){
mnp[i*p[j]]=p[j];
if (!(i%p[j]))
break;
}
}
n=input();
for (int i=1;i<=n;++i)
a[i]=input();
for (int i=1;i<=n;++i){
int t=a[i];
while (t!=1){
int x=mnp[t];
L[i]=max(L[i],lst[x]);
lst[x]=i;
while (!(t%x))
t/=x;
}
}
for (int i=1;i<=maxA;++i)
lst[i]=n+1;
for (int i=n;i>=1;--i){
R[i]=n+1;
int t=a[i];
while (t!=1){
int x=mnp[t];
R[i]=min(R[i],lst[x]);
lst[x]=i;
while (!(t%x))
t/=x;
}
}
st[top=1]=1;
for (int i=2;i<=n;++i){
if (R[st[top]]<=i){
printf("impossible\n");
return 0;
}
while (top && L[i]<st[top-1]+1 && R[i]>=R[st[top]]){
son[st[top]][1]=son[i][0];
son[i][0]=st[top];
top--;
}
son[st[top]][1]=i;
st[++top]=i;
}
for (int i=1;i<=n;++i)
fa[son[i][0]]=fa[son[i][1]]=i;
for (int i=1;i<=n;++i)
printf("%d ",fa[i]);
return 0;
}
总结
前面的那种分治做法应该是一个套路。
也就是\(T(n)=T(x)+T(n-x)+min(x,n-x)\)这种形式的分治,它的时间复杂度也是\(O(n\lg n)\)的。
其实我发现后面的这种做法有点像笛卡尔树……
这启示我们在建二叉树的过程中可以试试用栈维护最右边的那条链。
6368. 【NOIP2019模拟2019.9.25】质树的更多相关文章
- 6367. 【NOIP2019模拟2019.9.25】工厂
题目 题目大意 给你一堆区间,将这些区间分成特定的几个集合,使得每个集合中的所有区间的并不为空. 求最大的每组区间的交的长度之和. 思考历程 一开始就认为这绝对是\(DP\)-- 试着找一些性质,结果 ...
- 6359. 【NOIP2019模拟2019.9.15】小ω的树(tree)(定期重构)
题目描述 题解 qy的毒瘤题 CSP搞这种码农题当场手撕出题人 先按照边权从大到小建重构树,然后40%暴力修改+查找即可 100%可以定期重构+平衡规划,每次把B个询问拉出来建虚树,在虚树上暴力维护每 ...
- NOIP2019模拟2019.9.20】膜拜大会(外向树容斥,分类讨论)
传送门. 题解: 我果然是不擅长分类讨论,心态被搞崩了. 注意到\(m<=n-2\),意味着除了1以外的位置不可能被加到a[1]两遍. 先考虑个大概: 考虑若存在\(x,x-1,-,2\)(有序 ...
- [JZOJ6359] 【NOIP2019模拟2019.9.15】小ω的树
题目 题目大意 给你一棵树,带点权和边权. 要你选择一个联通子图,使得点权和乘最小边权最大. 支持修改点权操作. 思考历程 显然,最先想到的当然是重构树了-- 重构树就是在做最大生成树的时候,当两个联 ...
- 6424. 【NOIP2019模拟2019.11.13】我的订书机之恋
题目描述 Description Input Output Sample Input 见下载 Sample Output 见下载 Data Constraint 题解 lj题卡线段树 求出每个右端点往 ...
- 6389. 【NOIP2019模拟2019.10.26】小w学图论
题目描述 题解 之前做过一次 假设图建好了,设g[i]表示i->j(i<j)的个数 那么ans=∏(n-g[i]),因为连出去的必定会构成一个完全图,颜色互不相同 从n~1染色,点i的方案 ...
- 6364. 【NOIP2019模拟2019.9.20】养马
题目描述 题解 一种显然的水法:max(0,-(点权-边权之和*2)) 这样会挂是因为在中途体力值可能会更小,所以考虑求走完每棵子树所需的至少体力值 考虑从子树往上推求出当前点的答案 设每棵子树从根往 ...
- 6362. 【NOIP2019模拟2019.9.18】数星星
题目描述 题解 一种好想/好写/跑得比**记者还快的做法: 对所有询问排序,按照R递增的顺序来处理 维护每个点最后一次被覆盖的时间,显然当前右端点为R时的答案为所有时间≥L的点的权值之和 LCT随便覆 ...
- 6380. 【NOIP2019模拟2019.10.06】小w与最长路(path)
题目 题目大意 给你一棵树,对于每一条边,求删去这条边之后,再用一条边(自己定)连接两个连通块,形成的树的直径最小是多少. 正解 首先,将这棵树的直径给找出来.显然,如果删去的边不在直径上,那么答案就 ...
随机推荐
- 界面设计微信小程序
上一个周期将微信小程序的程序编完了,虽然还有很多需要修改的地方,大体已经完成.剩下的边角料以后再弄 这个周期来学习一下微信小程序的设计....好吧. 首先还是需要看一下微信的设计指南的.地址 http ...
- vue v-modle修饰符.number .trim
语法糖: 在不影响功能的情况下,添加某种方法实现同样的效果,从而方便程序开发. .number:可以将输入转换成Number类型,否则虽然输入的是数字,但它的类型其实是String. .trim:自动 ...
- 图像元数据编辑软件:MetaImage使用流程讲解
MetaImage是唯一的macOS工具,允许在处理所有类型的标签格式时编辑,读取和写入元数据.在时尚的界面中导航,您可以更改图像元数据的所有内容,并对数百张相似的照片进行相同的编辑. https:/ ...
- leetcood学习笔记-26-删除排序数组中的重复项
题目描述: 第一次提交: class Solution: def removeDuplicates(self, nums) -> int: for i in range(len(nums)-1, ...
- vue 项目 多选 问题
近期 vue 项目用到多选功能,引用的elementUI的级联多选 Cascader,但是没有效果. 后来发现是elementUI版本问题,我们项目用的是2.7.2版本,版本太低. 后来 卸载了 重新 ...
- Unity Document
{ https://docs.unity3d.com/ScriptReference/Events.UnityEventTools.html }
- SQL 循环语句几种写法
1.正常循环语句 declare @orderNum varchar(255)create table #ttableName(id int identity(1,1),Orders varchar( ...
- SQL 更新
SQL UPDATE 语句(更新表中的记录) UPDATE 语句用于更新表中的现有记录. SQL UPDATE 语句 UPDATE 语句用于更新表中已存在的记录. SQL UPDATE 语法 UPDA ...
- Shiro学习(12)与Spring集成
Shiro的组件都是JavaBean/POJO式的组件,所以非常容易使用spring进行组件管理,可以非常方便的从ini配置迁移到Spring进行管理,且支持JavaSE应用及Web应用的集成. 在示 ...
- mysql性能分析与故障分析
性能指标以及分析点:QPS,磁盘IO,cpu以及内存使用状况,连接数,慢查询,阻塞和锁等 qps的统计方法 ##使用方式 ,bash dba_qps.sh port(实例端口号) ## 脚本名 dba ...