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]);
}
posted @ 2019-09-27 21:54  Deep_Thinking  阅读(140)  评论(0编辑  收藏  举报