Atcoder AGC050A AtCoder Jumper 题解 [ 紫 ] [ 构造 ] [ adhoc ] [ 二叉树 ]
Atcoder Jumper:人类智慧题。
观察
首先观察到 \(2^{10}=1024>1000\),所以我们很显然能想到一个利用倍增或者类似线段树结构的一个东西来构造答案。
倍增想了想发现是不可行的,因为一个点最多拓展到另外两个点,并且倍增构造的话永远只能跳到 \(2\) 的次幂的点上,显然是不行的。
而根据一个点最多拓展到另外两个点,我们可以很自然地想到类似线段树的一个结构,即二叉树结构。
至此都很容易想到,我们以 \(1\) 为根构造二叉树,连边就是 \(2i\) 和 \(2i+1\) 的节点连单向边。但是这仅限于根节点为 \(1\) 的情况,如果是叶子结点,该咋办呢?
构造
我们考虑一个构造常用的思路,那就是把他归纳到原来的情况去,不难发现,从叶子结点出发,按照二叉树构造,且不限制深度的话,同样能到达另外 \(1024\) 个节点。那么我们就可以把这 \(1024\) 个节点重新分配到 \(1\sim n\) 里去。
具体的,我们可以将 \(2i\) 与 \(2i+1\) 对 \(n\) 取模后连边,注意特判 \(n\) 的情况。
为啥这个可以实现重新分配呢?因为在不考虑深度限制的情况下叶子结点 \(10\) 步内到达的节点可以组成一个模 \(1024\) 的剩余系,而这个对 \(n\) 取模后就必定能重新分配到每一个数去。
直接构造即可,时间复杂度 \(O(n)\)。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
int n;
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
int ls=(i<<1)%n,rs=((i<<1)|1)%n;
if(ls==0)ls=n;
if(rs==0)rs=n;
cout<<ls<<" "<<rs<<'\n';
}
return 0;
}

浙公网安备 33010602011771号