UOJ408 IOI2018 机械娃娃

Problem

UOJ

Solution

IOI的题很适合按subtask做

先考虑子任务3,对于每个触发器后面接的触发器,如果有4个,可以设计一个完全二叉树的结构,如果仅有3个只需要把XX这条边指向自己即可。这样构造出的开关不会超过 N N N

再考虑子任务4,N是2的整数次幂。不难发现子任务3的方法浪费了很多开关,不妨尝试对整个序列构建完全二叉树结构,把起点指向第一个触发器,所有触发器指向完全二叉树的根。

我们可以把同一层的深度的点向X走视作0,向Y走视作1,那么其实每走一次就相当于给当前代表的数进行+1操作,然后输出它的二进制串的反串能走到的地方,即第 i i i 次取出的节点就是 FFT \text{FFT} FFT 中的 r e v [ i ] rev[i] rev[i] 。不难发现我们会在这棵树中走完 0 ∼ 2 k − 1 0\sim 2^k-1 02k1,就保证了每个节点都被走过偶数次。

考虑子任务5,构建的完全二叉树中有很多叶子都是空的,一个显然的想法是如果一个开关子树内都是空的,我们可以直接把它指向根。为了节省更多的开关,我们会希望能把它集中在一起,那么我们就只取最右边的那些叶子即可。

计算一下,子树内没有空叶子的开关最多仅有 N N N 个,每层最多仅有1个不满的开关,因此答案不会超过 N + log ⁡ 2 N N+\log_2 N N+log2N

Code

#include "doll.h"
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
typedef vector<int> vec;
const int maxn=300010;
template <typename Tp> int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> void read(Tp &x)
{
	x=0;char ch=getchar();int f=0;
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') f=1,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();
	if(f) x=-x;
}
int n,N,l,tot,rt,rev[maxn],t[maxn],to[maxn];
vec c,x,y;
vector<int>::iterator itr;
int build(int l,int r)
{
	if(l==r) return l>=N-n?to[l]:-N;
	int m=(l+r)>>1,lc,rc;
	lc=build(l,m);rc=build(m+1,r);
	if(lc==-N&&rc==-N) return -N;
	x.push_back(lc);y.push_back(rc);
	return -x.size();
}
void create_circuit(int m,vec a)
{
	n=a.size();a.push_back(0);
	for(N=1,l=0;N<n;N<<=1) ++l;
	for(int i=0;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
	memset(t,0xff,sizeof(t));
	for(int i=N-n;i<N;i++) t[rev[i]]=i;
	for(int i=0;i<N;i++) if(~t[i]) to[t[i]]=a[++tot];
	rt=build(0,N-1);c.push_back(a[0]);
	for(int i=1;i<=m;i++) c.push_back(rt);
	for(itr=x.begin();itr!=x.end();++itr) if(*itr==-N) *itr=rt;
	for(itr=y.begin();itr!=y.end();++itr) if(*itr==-N) *itr=rt;
	answer(c,x,y);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值