[USACO22JAN] Tests for Haybales G

文章介绍了一道编程竞赛题目,涉及构造数组和最大位置的寻找。通过将问题转化为寻找大于给定值的位置,构建了一棵特殊的树,并利用深度优先搜索(DFS)来确定数组元素的值。题目要求找到满足特定条件的k和数组,其中k的范围是1到10^18,数组元素在0到10^18之间,数组大小不超过10^5。解决方案中,通过调整节点的值以满足层间差值要求,并确保DFS序与条件相符。

洛谷[USACO22JAN] Tests for Haybales G

题目大意

你要选定一个数 k k k并构造一个数组 x i x_i xi,满足 x 1 ≤ x 2 ≤ ⋯ ≤ x n x_1\leq x_2\leq \cdots\leq x_n x1x2xn,且对于每一个 i i i,都满足 j i j_i ji为使得 x j i ≤ x i + k x_{j_i}\leq x_i+k xjixi+k的最大的位置。给出 j i j_i ji,并满足 i ≤ j i i\leq j_i iji以及 j 1 ≤ j 2 ≤ ⋯ ≤ j n ≤ n j_1\leq j_2\leq \cdots\leq j_n\leq n j1j2jnn

输出 k k k和数组 x i x_i xi,需满足 1 ≤ k ≤ 1 0 18 1\leq k\leq 10^{18} 1k1018 0 ≤ x i ≤ 1 0 18 0\leq x_i\leq 10^{18} 0xi1018

1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105


题解

首先,我们可以将 j i j_i ji都加上 1 1 1,那其意义就变为了第一个大于 x i + k x_i+k xi+k的位置。

建一棵 n + 1 n+1 n+1个节点的数,对于每个节点 i i i,以 j i + 1 j_i+1 ji+1作为父亲并连一条边,那么根节点就为 n + 1 n+1 n+1

对于每一层,我们需要这一层的节点比它的每一个儿子的值大 k k k以上。那么,我们可以将深度为 d e p dep dep的点的 x x x值定为 ( n + 1 − d e p ) × k + t ( 0 ≤ t < k ) (n+1-dep)\times k+t(0\leq t <k) (n+1dep)×k+t(0t<k)

t t t需要满足:

  • 点的深度越大, t t t越小
  • 点的标号越大, t t t越大

我们发现, d f s dfs dfs序与这个条件正好相反。那么,我们令 t = k − d f n i t=k-dfn_i t=kdfni。因为连边时是从小到大,所以遍历时是从大到小,编号越大, d f n i dfn_i dfni就越小, k − d f n i k-dfn_i kdfni就越大。

需要满足 k > n k>n k>n,还要注意 x i x_i xi的范围。

时间复杂度为 O ( n ) O(n) O(n)

code

#include<bits/stdc++.h>
using namespace std;
int n,x,tot=0,dfn=0,d[200005],l[200005],r[200005];
long long k=1e6,v[200005],ans[200005];
void add(int xx,int yy){
	l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void dfs(int u){
	ans[u]=v[u]*k-(++dfn);
	for(int i=r[u];i;i=l[i]){
		v[d[i]]=v[u]-1;
		dfs(d[i]);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		add(x+1,i);
	}
	v[n+1]=n+1;dfs(n+1);
	printf("%lld\n",k);
	for(int i=1;i<=n;i++){
		printf("%lld\n",ans[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值