传送门biu~
预处理每个点能作为树根的区间,即预处理出每一个数左边和右边第一个与它不互质的数的位置。可以发现对于一棵子树,以任一合法的点作为根都是一定成立的。用【bzoj 4059】Non-boring sequences的启发式分裂思想进行递归验证。时间复杂度
O(nlogn)
O
(
n
log
n
)
。
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
int n,Max,a[N],pre[10*N],f[10*N],fa[N],prime[10*N],Ls[N],Rs[N],tp;
bool b[10*N];
inline void Get_Prime(){
for(int i=2;i<=Max;++i){
if(!b[i]) prime[++tp]=i,f[i]=i;
for(int j=1;j<=tp && 1ll*i*prime[j]<=Max;++j){
b[i*prime[j]]=true; f[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
bool solve(int l,int r,int f){
if(l>r) return true;
int x=l,y=r;
while(x<=y){
if(Ls[x]<l && Rs[x]>r){
fa[x]=f;
return solve(l,x-1,x) && solve(x+1,r,x);
}
if(Ls[y]<l && Rs[y]>r){
fa[y]=f;
return solve(l,y-1,y) && solve(y+1,r,y);
}
++x,--y;
}
return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),Max=max(Max,a[i]);
Get_Prime();
for(int i=1;i<=n;++i){
int now=a[i],L=0;
while(now^1){
int g=f[now];
L=max(L,pre[g]);
pre[g]=i;
while(now%g==0) now/=g;
}
Ls[i]=L;
}
for(int i=1;i<=Max;++i) pre[i]=n+1;
for(int i=n;i>=1;--i){
int now=a[i],R=n+1;
while(now^1){
int g=f[now];
R=min(R,pre[g]);
pre[g]=i;
while(now%g==0) now/=g;
}
Rs[i]=R;
}
if(solve(1,n,0))
for(int i=1;i<=n;++i) printf("%d ",fa[i]);
else puts("impossible");
return 0;
}