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)
题目 题目大意 给你一棵树,对于每一条边,求删去这条边之后,再用一条边(自己定)连接两个连通块,形成的树的直径最小是多少. 正解 首先,将这棵树的直径给找出来.显然,如果删去的边不在直径上,那么答案就 ...
随机推荐
- 关于KiCAD的一些插件
关于KiCAD的一些插件 https://gitee.com/KiCAD-CN/KiCad-CN-Forum/blob/master/KiCad_help_zh_CN.md#swapit-%E5%B7 ...
- Cloudflare-为自己的网站&博客保驾护航
官网: https://www.cloudflare.com/zh-cn/ Cloudflare是什么? 优化功能,提升网站性能,提供SSL服务,提供安全防护,托管服务:为企业,非营利组织,博客等提供 ...
- Python3:_pickle使用方法
常遇到的问题: python3使用pickle读取文件提示TypeError或者UnicodeDecodeError的解决办法 “ModuleNotFoundError: No module name ...
- python 对列表中任意两个数进行操作 (python operate any two elements in list)
python中, 实现列表中的整型元素两两相乘或列表中的数组元素两两相与 1. 假设列表中的元素是整型, 可调用以下函数: def list_any_two_mul(mylist): num ...
- 线程池——Executors
一 Executor框架 为了更好地控制多线程,JDK提供了一套线程框架Executor,帮助开发人员有效的进行线程控制.它们都在java.util.concurrent包中,是JDK并发包的核心.其 ...
- 树的直径变形——cf1238F
/* 题目给定一些一维线段[li,ri],要求从这些线段里挑出一些线段,每条线段对应一个点,如果两线段相交,那么点连边,这样得到的树是good-tree 现在给定一棵树,要求从该树中选出一棵子树,使这 ...
- Python读取文件时出现UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position xx: 解决方案
Python在读取文件时 with open('article.txt') as f: # 打开新的文本 text_new = f.read() # 读取文本数据 出现错误: UnicodeDecod ...
- django-filters,rangefilter的用法,不在某个范围内
找了好久在网上都没找到完整的示例代码,东拼西凑,连蒙带猜出来一句. not_in=django_filters.NumericRangeFilter(field_name="pid" ...
- mybatis源码探究(-)MapperProxyFactory&MapperProxy
在MyBatis中MapperProxyFactory,MapperProxy,MapperMethod是三个很重要的类. 弄懂了这3个类你就大概清楚Mapper接口与SQL的映射, 为什么是接口,没 ...
- jQuery 加载事件
1. jquery加载事件实现 ① $(document).ready(function处理); ② $().ready(function处理); ③ $(function处理); 对第一种加载的封 ...