题目链接:

IOI2018doll

题目大意:有一个起点和$m$个触发器,给出一个长度为$n$的序列$a$,要求从起点出发按$a$的顺序经过触发器并回到起点(一个触发器可能被经过多次也可能不被经过),起点和每个触发器都有一个出口和若干个入口。你可以在这些触发器之间加上一些开关,每个开关有两个出口$x,y$和若干个入口,当奇数次进入开关时会从$x$出来,当偶数次进入开关时会从$y$出来,要求第一次回到起点时所有开关都被经过偶数次且使用的开关数尽量少,输出每个元件的出口指向。

因为最后要求回到起点,所以我们将序列$a$后面再加上一个起点。当$n=2^k$的时候可以构造一个类似线段树的满二叉树结构的装置。起点指向线段树的根,对于线段树的每个非叶字节点是一个开关,$x$出口指向左儿子,$y$出口指向右儿子;对于每个叶子节点是一个触发器(如果一个触发器在序列$a$中出现多次那么在线段树上就可以看作将它分成好几个点),他们的出口都指向线段树的根节点。这样就满足了每个非叶字节点即开关都经过了偶数次的条件。可以发现对于$a_{i}(0\le i \le n-1)$,假设它在线段树上位于第$x$个叶子,那么$i$的二进制倒序就是$x$,这个类似$FFT$的蝶形变换。所以我们只需要一次蝶形变换就能知道叶子结点的父节点左右儿子分别是谁。那么如果$n$不是$2$的整数次幂怎么办?我们同样蝶形变换将序列$a$的每一位填入线段树的叶子结点中,对于空的叶子结点就可以将这个点父节点的对应出口直接指向线段树的根节点就好了。这样我们得到了一个最坏情况下$2n$的做法,不过可以发现如果一个非叶字节点的两个出口都指向根节点那么这个开关就是无用的,可以将它删除然后将它父节点的对应出口指向根节点。这样虽然使节点数有所减少但还是达不到$n+logn$的要求。可以发现许多非叶子节点只有一个子节点,如果尽可能的使每个非叶子节点都有两个子节点那么可以节省许多的开关。实际上只要将这些放置了触发器的叶子结点按顺序地移到前$n$个叶子节点或后$n$个叶子结点即可,我们以移到前$n$个叶子结点为例说明(代码中移到了后n个叶子结点)。最后一个叶子结点从$0$开始的标号为$n-1$(除去我们在序列后面加的起点就是题目中给出的序列长度$n$),对于这个叶子结点到根的这条链,可以看做是这个节点从根走到叶子。我们将$n$拆成二进制,可以发现从左开始如果第$i$位为$0$那么在线段树的第$i$层它就会向左走,反之就会向右走。假设从右开始的第$i$位为$1$,那么这一次它就会向右走,这就说明这个点有左子树,那么节点数就会加上这个点的左子树大小即$2^i-1$,而向左走则除去这条链上的点没有其他节点数的贡献。可以发现每个有贡献的$2^i$对应了$n$二进制上的$1$,这条链上有$logn-1$个非叶子节点,所以总非叶子节点数为$O(n+logn)$。也可以将序列a的第一个数拿出来,将起点指向$a_{0}$,$a_{0}$再指向线段树的根。

#include"doll.h"
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define INF 100000000
using namespace std;
int n,m;
int rev[800010];
int mask;
int cnt;
int rt;
int s[800010];
int t[800010];
vector<int>to;
vector<int>a,c,x,y;
int solve(int l,int r)
{
if(l==r)
{
return l>=mask-n?a[t[l]]:INF;
}
int mid=(l+r)>>1;
int ls=solve(l,mid);
int rs=solve(mid+1,r);
if(ls==INF&&rs==INF)
{
return INF;
}
else
{
x.push_back(ls);
y.push_back(rs);
return --cnt;
}
}
void create_circuit(int M,vector<int> A)
{
n=A.size();
A.push_back(0);
m=M;
a=A;
mask=1;
while(mask<n)
{
mask<<=1;
}
for(int i=0;i<mask;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)?(mask>>1):0);
s[i]=-1;
}
for(int i=mask-n;i<mask;i++)
{
s[rev[i]]=i;
}
for(int i=0;i<mask;i++)
{
if(s[i]!=-1)
{
to.push_back(s[i]);
}
}
for(int i=0;i<n;i++)
{
t[to[i]]=i+1;
}
rt=solve(0,mask-1);
c.resize(m+1,rt);
c[0]=a[0];
int len=x.size();
for(int i=0;i<len;i++)
{
x[i]==INF?x[i]=rt:1;
y[i]==INF?y[i]=rt:1;
}
answer(c,x,y);
}

[IOI2018]机械娃娃——线段树+构造的更多相关文章

  1. [IOI2018]会议——分治+线段树

    题目链接: [IOI2018]meetings 题目大意:有$n$座山峰,每座山峰有一个高度,有$q$次询问,每次需要确定一个开会山峰使$[l,r]$所有山峰上的人都前往开会山峰,一个山峰的人去开会的 ...

  2. Codeforces482B【线段树构造】

    题意: 有M个限制,每个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是否有满足的数列. 思路: 看到大牛说是线段树,线段树对于区间操作,印象中乘啊,+啊,-啊都不错,但是并没有就是 ...

  3. LOJ.2864.[IOI2018]排座位(线段树)

    LOJ 洛谷 先令编号从\(1\)开始.我们要求\([1,i]\)这些数字能否构成一个矩形. 考虑能否用线段树维护,让每个叶子节点\(i\)表示前\(i\)个数能否构成矩形. 一种方法是维护前\(i\ ...

  4. [IOI2018]排座位——线段树

    题目链接: IOI2018seat 题目大意:给出一个$H*W$的矩阵,将$0 \sim W*H-1$分别填入矩阵的格子里(每个格子里一个数),定义一个子矩阵是美妙的当且仅当这个子矩阵包含且仅包含$0 ...

  5. CF R638 div2 F Phoenix and Memory 贪心 线段树 构造 Hall定理

    LINK:Phoenix and Memory 这场比赛标题好评 都是以凤凰这个单词开头的 有凤来仪吧. 其实和Hall定理关系不大. 不过这个定理有的时候会由于 先简述一下. 对于一张二分图 左边集 ...

  6. codeforces 671C Ultimate Weirdness of an Array 线段树+构造

    题解上说的很清楚了,我照着写的,表示膜拜题解 然后时间复杂度我觉得应该是O(nlogn),虽然常数略大,预处理和倒着扫,都是O(nlogn) #include <stdio.h> #inc ...

  7. 线段树(segment_tree)

    线段树之——区间修改区间查询 1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lgN). 线段树是 ...

  8. HDU1166 敌兵布阵_线段树

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  9. 线段树(Segment Tree)(转)

    原文链接:线段树(Segment Tree) 1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即“子数组”),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lg ...

随机推荐

  1. 立足中国,走向世界(Made in China, Go to World)

    FineUI一路走来已经历经 9 年的风风雨雨,拥有国内最为广泛的捐赠群体(1500多位),和众多企业客户的青睐(200多家). 今天,我们很高兴的宣布:FineUI英文版上线了! FineUI英文版 ...

  2. centos7 关闭selinux

    关闭SeLinux 临时关闭:setenforce 0 永久关闭:vi /etc/selinux/config

  3. python四:函数练习--小白博客

    为什么要有函数?函数式编程定义一次,多出调用函数在一定程度上可以理解为变量函数的内存地址加上()就是调用函数本身也可以当做参数去传参 不用函数:组织结构不清晰代码的重复性 def test():#te ...

  4. Python并发编程

    进程 相关概念 进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.在早期面向进程设计的计算机结构中,进程是程序的基本 ...

  5. .net WCF WF4.5

    花了两天时间学习使用WF,把一些遇到的问题记录下来,使用的环境是VS2017,网上的资料普遍太老了 需要注意,如果使用多项目同时启动的方式需要把WCF调整到WF启动顺序之上 1.怎么使用代码活动 新建 ...

  6. 3proxy使用方法

    转自:DRL@fireinice写的教程 ******************************************************************************* ...

  7. C#设计模式之5:简单工厂和工厂方法模式

    工厂模式包含三种,简单工厂模式,工厂方法模式,抽象工厂模式.这三种都是解决了一个问题,那就是对象的创建问题.他们的职责就是将对象的创建和对象的使用分离开来. 当我们创建对象的时候,总是会new一个对象 ...

  8. scp复制文件到远程服务器上

    scp -P 22 -r 2028792_www  root@120.79.172.45:/usr/local/src Linux scp命令用于Linux之间复制文件和目录. scp是 secure ...

  9. Linux 文件特殊权限 SUID SGID SBIT

    文件除了常规的权限r, w, x 还有一些特殊的权限,s与t权限,具体的用处如下 1 SetUID 当s 这个标志出现在文件所有者的x权限上时, 例如/usr/bin/passwd, [root@or ...

  10. Linux下面将windows写的脚本转换成 Linux 格式的文件

    1.接着上一篇blog 发现有一个问题 vim 打开文件 发现最下面有一行信息: 2. 里面有一些 不一样的地方. linux里面没法执行 ,如果想执行的话 可以输入命令 :set fileforma ...