JZOJ6368. 【NOIP2019模拟2019.9.25】质树(tree)
Description
大神 wyp 手里有棵二叉树,每个点有一个点权。大神 wyp 的这棵树是质树,因为
随便找两个不同的点 u, v,只要 u 是 v 的祖先,都满足 u 和 v 的点权互质。
现在你通过偷看了解到了大神 wyp 这棵树的中序遍历的点权值,你想复原出大神
wyp 的树,或者指出不可能。
n<=1e6
Solution
- 每一次取一个区间中的与其他位置互质的位置为根,如果有多个显然都可以。
- 只用预处理左边右边第一个与它不互质的就可以O(1)判。
- 如果直接暴力找每个点的话是n2的。
- 我们只要从两边搜同时搜这个互质的位置就好了。
- T(n)=T(x)+T(n-x)+min(x,n-x)
- 跟dsu on tree (一种启发式合并)的时间是类似的。
- 每个点最多贡献log。
- O(n log n)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 1000005
#define maxm 10000005
using namespace std;
int n,i,j,k,x,a[maxn],f[maxn],L[maxn],R[maxn];
int bz[maxm],tot,pri[maxm],g[maxm],nx[maxm];
void read(int &x){
x=0; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}
void Get_Prime(){
for(int i=2;i<maxm;i++) {
if (!bz[i]) {pri[++tot]=i;g[i]=i;}
for(int j=1;j<=tot&&i*pri[j]<maxm;j++) {
bz[i*pri[j]]=1;
g[i*pri[j]]=pri[j];
if (i%pri[j]==0) break;
}
}
}
int pd(int l,int r,int x){
return L[x]<l&&R[x]>r;
}
void Solve(int l,int r,int x){
if (l==r) f[l]=x;
if (l>r) return;
for(int len=0;len<=(r-l+1)/2;len++){
int i=l+len,j=r-len;
if (pd(l,r,i)){
Solve(l,i-1,i);
Solve(i+1,r,i);
f[i]=x; return;
}
if (pd(l,r,j)){
Solve(l,j-1,j);
Solve(j+1,r,j);
f[j]=x; return;
}
}
printf("impossible");
exit(0);
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
Get_Prime();
read(n);
for(i=1;i<=n;i++) read(a[i]);
for(i=1;i<=n;i++) {
L[i]=0,x=a[i];
while (x>1){
if (nx[g[x]]!=i) L[i]=max(L[i],nx[g[x]]);
nx[g[x]]=i,x/=g[x];
}
}
memset(nx,0,sizeof(nx));
for(i=n;i>=1;i--) {
R[i]=n+1,x=a[i];
while (x>1){
if (nx[g[x]]!=i&&nx[g[x]]) R[i]=min(R[i],nx[g[x]]);
nx[g[x]]=i,x/=g[x];
}
}
Solve(1,n,0);
for(i=1;i<=n;i++) printf("%d ",f[i]);
}