题目

题目大意

有个二叉树,满足每个点跟它的所有祖先互质。

给出二叉树的中序遍历的点权,还原一种可能的方案。


思考历程

首先想到的当然是找到一个跟全部互质的点作为根,然后左右两边递归下去处理……

然而考虑到和全部互质的点可能有很多个,这样的做法可能会退化到很多……

先预处理了个\(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】质树的更多相关文章

  1. 6367. 【NOIP2019模拟2019.9.25】工厂

    题目 题目大意 给你一堆区间,将这些区间分成特定的几个集合,使得每个集合中的所有区间的并不为空. 求最大的每组区间的交的长度之和. 思考历程 一开始就认为这绝对是\(DP\)-- 试着找一些性质,结果 ...

  2. 6359. 【NOIP2019模拟2019.9.15】小ω的树(tree)(定期重构)

    题目描述 题解 qy的毒瘤题 CSP搞这种码农题当场手撕出题人 先按照边权从大到小建重构树,然后40%暴力修改+查找即可 100%可以定期重构+平衡规划,每次把B个询问拉出来建虚树,在虚树上暴力维护每 ...

  3. NOIP2019模拟2019.9.20】膜拜大会(外向树容斥,分类讨论)

    传送门. 题解: 我果然是不擅长分类讨论,心态被搞崩了. 注意到\(m<=n-2\),意味着除了1以外的位置不可能被加到a[1]两遍. 先考虑个大概: 考虑若存在\(x,x-1,-,2\)(有序 ...

  4. [JZOJ6359] 【NOIP2019模拟2019.9.15】小ω的树

    题目 题目大意 给你一棵树,带点权和边权. 要你选择一个联通子图,使得点权和乘最小边权最大. 支持修改点权操作. 思考历程 显然,最先想到的当然是重构树了-- 重构树就是在做最大生成树的时候,当两个联 ...

  5. 6424. 【NOIP2019模拟2019.11.13】我的订书机之恋

    题目描述 Description Input Output Sample Input 见下载 Sample Output 见下载 Data Constraint 题解 lj题卡线段树 求出每个右端点往 ...

  6. 6389. 【NOIP2019模拟2019.10.26】小w学图论

    题目描述 题解 之前做过一次 假设图建好了,设g[i]表示i->j(i<j)的个数 那么ans=∏(n-g[i]),因为连出去的必定会构成一个完全图,颜色互不相同 从n~1染色,点i的方案 ...

  7. 6364. 【NOIP2019模拟2019.9.20】养马

    题目描述 题解 一种显然的水法:max(0,-(点权-边权之和*2)) 这样会挂是因为在中途体力值可能会更小,所以考虑求走完每棵子树所需的至少体力值 考虑从子树往上推求出当前点的答案 设每棵子树从根往 ...

  8. 6362. 【NOIP2019模拟2019.9.18】数星星

    题目描述 题解 一种好想/好写/跑得比**记者还快的做法: 对所有询问排序,按照R递增的顺序来处理 维护每个点最后一次被覆盖的时间,显然当前右端点为R时的答案为所有时间≥L的点的权值之和 LCT随便覆 ...

  9. 6380. 【NOIP2019模拟2019.10.06】小w与最长路(path)

    题目 题目大意 给你一棵树,对于每一条边,求删去这条边之后,再用一条边(自己定)连接两个连通块,形成的树的直径最小是多少. 正解 首先,将这棵树的直径给找出来.显然,如果删去的边不在直径上,那么答案就 ...

随机推荐

  1. Centos7上MariaDB数据库启动问题解决

    安装MariaDB数据库后出现服务启动失败问题, 解决办法:卸载再安装!(确定无3306端口占用) 一.卸载数据库: [root@localhost logs]# yum -y remove mari ...

  2. ANdroid手机屏幕反横向等参数设定

    经过我一番百度和看Android文档,我才发现,Android对旋转屏,特别是只有横屏或者竖屏虽重力旋转的支持是到Android4.3.1才有完美支持的 unspecified - 默认值,由系统选择 ...

  3. Qt 【“QWebView/private/qwebview interface p.h”: No such file or directory】

    这种情况下需要在pro工程文件中添加 QT += webkitwidgets 然后清理当前工程, 重新构建,在运行即可. 如果还不行,那么在#include <QWebView>这样替换成 ...

  4. VS2017编译64位CloudCompare

    需求:编译一个支持读写las点云的CC,然后再开发CC插件实现业务功能. 编译环境: 1.Windows 10 2.Visual Studio 2017 Community 3.Qt 5.9.4 开源 ...

  5. SpringBoot集成Swagger(Swagger的使用),生成接口文档,方便前后端分离开发

    首先上一张成果图.  1.Maven依赖 <dependency> <groupId>io.springfox</groupId> <artifactId&g ...

  6. rem与px的相爱相杀

    rem 简介:为元素设定字体大小:相对大小:只相对HTML根元素. 优点:只修改根元素就可以成比例调整所有字体的大小. bug:IE8及更早版本不兼容. hack:多写一个绝对单位px的声明,不兼容版 ...

  7. 操作系统之IO管理

    IO系统结构 设备的分类 按数据组织分 块设备: 信息的存取总是以数据块为单位. 它属于有结构设备,如磁盘等. 磁盘设备的基本特征是传输速率较高,以及可寻址,即对它可随机地读/写任一块. 字符设备: ...

  8. 锐浪报表 Grid++Report 一维码无法固定条形码打印宽度

    使用过 锐浪报表的 程序员 都知道,功能很强大,确实带来了很多便利,但今天发现一个问题,关于一维码的条形码无法固定宽度: 打印相差了0.07毫米,居然差别这么大, 打印出来的条码,要么太宽,要么太窄 ...

  9. 分布式项目pom

    <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit ...

  10. JSP自定义标签(转)

    1. TagSupport与BodyTagSupport的区别 TagSupport与BodyTagSupport的区别主要是标签处理类是否需要与标签体交互,如果不需要交互的就用TagSupport, ...