[CF_GYM101623F]Factor-Free Tree

220 篇文章 2 订阅
98 篇文章 0 订阅

题目

传送门 to CF

题目概要
给出一棵树的中序遍历,问,能否还原出这棵树,满足树上任意有祖先关系的不同两点 a , b a,b a,b gcd ⁡ ( v a , v b ) = 1 \gcd(v_a,v_b)=1 gcd(va,vb)=1 。如果有,输出这棵树。

数据范围与提示
n ≤ 1 0 6 n\le 10^6 n106 v i ≤ 1 0 7 v_i\le 10^7 vi107 。时间限制 6 s 6\rm s 6s

思路

不难想到一个 d p \tt dp dp 的思路, f ( l , r ) f(l,r) f(l,r) 表示中序遍历结果中第 l l l 个到第 r r r 个能否构建出 “互质树”。
f ( l , r ) = max ⁡ i = l r f ( l , i − 1 ) × f ( i + 1 , r ) × [ ∀ j ,    gcd ⁡ ( v i , v j ) = 1 ] f(l,r)=\max_{i=l}^{r}f(l,i-1)\times f(i+1,r)\times [\forall j,\;\gcd(v_i,v_j)=1] f(l,r)=i=lmaxrf(l,i1)×f(i+1,r)×[j,gcd(vi,vj)=1]

这个是 O ( n 4 ) \mathcal O(n^4) O(n4) 的,直接起飞。考虑优化。比如,我们容易发现
f ( l − 1 , r ) = 1 ∨ f ( l , r + 1 ) = 1 ⇒ f ( l , r ) = 1 f(l-1,r)=1\vee f(l,r+1)=1 \Rightarrow f(l,r)=1 f(l1,r)=1f(l,r+1)=1f(l,r)=1

正确性似乎是显然的,我还是简要说两句。考虑 f ( l , r + 1 ) f(l,r{\rm+}1) f(l,r+1) 对应的 “互质树”,第 r + 1 r+1 r+1 号节点必然没有右子树(否则其右子树是中序遍历更靠后的),删掉 r + 1 r+1 r+1 节点并连接其父节点与其左儿子,生成的就是 f ( l , r ) f(l,r) f(l,r) 对应的树。得到的新树肯定仍然是合法的。

你可能没意识到这句话意味着什么。那我告诉你,这就是说,如果当前区间可行,那么所有子区间都可行。所以,更新 f ( l , r ) f(l,r) f(l,r) 时,只需要任取一个合法的 i i i,如果不行,就说明 f ( l , r ) = 0 f(l,r)=0 f(l,r)=0 了。

现在的问题是,怎么找 i i i 呢?想 O ( 1 ) \mathcal O(1) O(1) 比较困难。一个比较简单的方法是利用启发式合并,在 O [ min ⁡ ( i , n − i ) ] \mathcal O[\min(i,n-i)] O[min(i,ni)] 的复杂度内完成即可。答案就是 两边向中间找

判断就很简单了,每个数质因数分解一下,利用 O ( V ) \mathcal O(V) O(V) 线性筛后 O ( n log ⁡ V ) \mathcal O(n\log V) O(nlogV) 分解。对于每种质因数,把数组扫一遍。而后我们求得每个数字的控制范围。而后可以 O ( 1 ) \mathcal O(1) O(1) 判断 i i i 是否为根。

递归的复杂度
T ( n ) = T ( i ) + T ( n − i ) + O [ min ⁡ ( i , n − i ) ] = O ( n log ⁡ n ) T(n)=T(i)+T(n-i)+\mathcal O[\min(i,n-i)]=\mathcal O(n\log n) T(n)=T(i)+T(ni)+O[min(i,ni)]=O(nlogn)

总复杂度 O ( V + n log ⁡ V + n log ⁡ n ) \mathcal O(V+n\log V+n\log n) O(V+nlogV+nlogn)

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxV = 10000001;
int nxt[MaxV], least[MaxV];
vector<int> primes;
void sieve(int n = MaxV-1){
	for(int i=2; i<=n; ++i)
		nxt[i] = 1, least[i] = i;
	for(int i=2,len=0; i<=n; ++i){
		if(least[i] == i) ++ len,
			primes.push_back(i);
		for(int j=0; j<len&&primes[j]<=n/i; ++j){
			nxt[i*primes[j]] = i;
			least[i*primes[j]] = primes[j];
			if(i%primes[j] == 0){
				nxt[i*primes[j]] = nxt[i];
				break;
			}
		}
	}
}

vector<int> v[MaxV];
const int MaxN = 1000001;
int L[MaxN], R[MaxN];

int fa[MaxN];
int solve(int l,int r){
	if(l == r) return l; // leaf
	if(l > r) return 0;
	for(int i=0; l+i<=r-i; ++i){
		if(L[l+i] < l && r < R[l+i]){
			int lson = solve(l,l+i-1);
			int rson = solve(l+i+1,r);
			if(!(~lson) || !(~rson))
				return -1; // no such tree
			fa[lson] = fa[rson] = l+i;
			return l+i; // root
		}
		if(L[r-i] < l && r < R[r-i]){
			int lson = solve(l,r-i-1);
			int rson = solve(r-i+1,r);
			if(!~lson || !~rson)
				return -1; // no such tree
			fa[lson] = fa[rson] = r-i;
			return r-i; // root
		}
	}
	return -1; // no such tree
}

int main(){
	sieve(); // prepare
	int n = readint();
	for(int i=1; i<=n; ++i){
		int x = readint();
		for(; x!=1; x=nxt[x])
			v[least[x]].push_back(i);
		L[i] = 0, R[i] = n+1; // (L,R)
	}
	for(int i=0; i<MaxV; ++i){
		int len = v[i].size();
		for(int j=0; j+1<len; ++j){
			R[v[i][j]] = min(R[v[i][j]],v[i][j+1]);
			L[v[i][j+1]] = max(L[v[i][j+1]],v[i][j]);
		}
	}
	if(solve(1,n) == -1)
		puts("impossible");
	else{
		for(int i=1; i<=n; ++i)
			printf("%d ",fa[i]);
		putchar('\n');
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值