【LOJ2866】「IOI2018」机械娃娃

【题目链接】

【思路要点】

  • 将起点连向第一个触发器,每一个触发器连向同一个开关 r o o t root root
  • 构造一棵 i i i 层的满二叉树,使得 2 i ≥ N 2^i≥N 2iN ,这样,如果 r o o t root root 被经过了 2 i 2^i 2i 次,那么所有节点都会回到 X X X 状态,是符合条件的。
  • 我们将第 1 , 2 , . . . , 2 i − N 1,2,...,2^i-N 1,2,...,2iN 次经过 r o o t root root 走到的叶子结点连向 r o o t root root ,将第 2 i − N + i 2^i-N+i 2iN+i 次经过 r o o t root root 走到的叶子结点连向 a i a_i ai ,即可得到一个使用节点数不超过 2 N 2N 2N 的做法。
  • 稍加思考,我们发现,若一个子树内所有的叶子结点均连向 r o o t root root ,那么我们可以将这个子树删除,将这个子树的根节点连向 r o o t root root 。于是,类似于线段树的区间定位,我们将靠左的 2 i − N 2^i-N 2iN 个节点用这种方式连向 r o o t root root 即可。
  • 需要特判 N = 1 N=1 N=1 的情况。
  • 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) ,使用节点数不超过 N + L o g 2 N N+Log_{2}N N+Log2N

【代码】

#include "doll.h"
#include<bits/stdc++.h>
const int MAXM = 1e5 + 5;
const int MAXP = 4e5 + 5;
using namespace std;
vector <int> c, x, y;
int n, m, p, root, waste;
bool state[MAXP];
void build(int &pos, int tot) {
	pos = ++p;
	state[p] = false;
	if (tot == 2) return;
	int tmp = tot >> 1;
	if (tmp > waste) build(x[pos - 1], tot >> 1), x[pos - 1] *= -1;
	else {
		waste -= tmp;
		x[pos - 1] = -root;
	}
	build(y[pos - 1], tot >> 1), y[pos - 1] *= -1;
}
bool link(int pos, int tot, int dest) {
	state[pos] ^= true;
	if (tot == 2) {
		if (state[pos]) x[pos - 1] = dest;
		else y[pos - 1] = dest;
		return true;
	}
	if (state[pos]) {
		if (-x[pos - 1] == root) return false;
		else return link(-x[pos - 1], tot >> 1, dest);
	} else {
		if (-y[pos - 1] == root) return false;
		else return link(-y[pos - 1], tot >> 1, dest);
	}
}
void create_circuit(int tm, vector <int> list) {
	n = list.size(), m = tm;
	list.push_back(0);
	c.resize(m + 1), c[0] = list[0];
	x.resize(n + log2(n));
	y.resize(n + log2(n));
	if (n == 1) {
		c[list[0]] = 0;
		answer(c, x, y);
		return;
	}
	int tot = 1;
	while (tot < n) tot <<= 1;
	waste = tot - n;
	build(root, tot);
	for (int i = 1; i <= m; i++)
		c[i] = -root;
	assert(waste <= 1);
	for (int i = 1; i <= waste; i++)
		while (!link(root, tot, -root));
	for (int i = 1; i <= n; i++)
		while (!link(root, tot, list[i]));
	answer(c, x, y);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值