BZOJ3990 [SDOI2015]排序 【搜索】
题目
小A有一个1-2N的排列A[1..2N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1<=i<=N),第i中操作为将序列从左到右划分为2{N-i+1}段,每段恰好包括2{i-1}个数,然后整体交换其中两段.小A想知道可以将数组A从小到大排序的不同的操作序列有多少个,小A认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同).
下面是一个操作事例:
N=3,A[1..8]=[3,6,1,2,7,8,5,4].
第一次操作,执行第3种操作,交换A[1..4]和A[5..8],交换后的A[1..8]为[7,8,5,4,3,6,1,2].
第二次操作,执行第1种操作,交换A[3]和A[5],交换后的A[1..8]为[7,8,3,4,5,6,1,2].
第三次操作,执行第2中操作,交换A[1..2]和A[7..8],交换后的A[1..8]为[1,2,3,4,5,6,7,8].
输入格式
第一行,一个整数N
第二行,2N个整数,A[1..2N]
输出格式
一个整数表示答案
输入样例
3
7 8 5 6 1 2 4 3
输出样例
6
提示
100%的数据, 1<=N<=12.
题解
我们可以大力猜想出操作的顺序可以是任意的
随便试试就会发现
仔细观察发现,所有的操作区间只有 完全包含关系 or 不相交
对于一个合法操作序列中的相邻两个操作\(a\)和\(b\),我们尝试交换其顺序
①若\(a\)、\(b\)不相交,那么显然无影响
②若\(a\)、\(b\)相交,那么一定是包含关系,不妨设\(|a| < |b|\)
如果\(a\)的一个操作区间在\(b\)中,那么在操作\(b\)前后交换\(a\)中的两个区间显然不改变顺序
如果\(a\)的两个操作区间都在\(b\)中,那么在\(b\)操作前后这两个区间内的元素是不变的,我们只需在\(b\)操作之后找到原来的两个区间进行交换,最后的序列仍然不变
这就粗略地证明了
既然顺序无关,我们就可以从小枚举了
因为大区间操作无法影响其内部,所以我们每一次操作都要保证下一级区间内部一定是按+1递增顺序的
具体地,对于第\(i\)种操作,操作区间长度为\(2^{i - 1}\),那么我们找到第\(i + 1\)种操作的所有区间,如果其内部不是按+1递增的,那么这个区间一定要被操作
如果这样的区间数量\(>3\),显然我们是无法全部顾及的,直接返回
如果这样的区间数量为1,那么只需要交换这个区间内部
如果这样的区间数量为2,若存在合法方案,一定是交换这两个区间\(2\)个子区间的一个,共有\(4\)中情况,逐一检验即可
最后,如果一个操作集合\(S\)合法,那么将贡献\(|S|!\)的方案数
如果说每一层只会有一种情况合法的话,总的复杂度\(O(n * 2^n)\)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 13,maxm = 10000,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int a[maxm],n,N,chs;
LL ans,fac[maxn];
bool isorder(int l,int len){
for (int i = 1; i < len; i++) if (a[l + i] != a[l + i - 1] + 1) return false;
return true;
}
void Swap(int u,int v,int len){
for (int i = 0; i < len; i++) swap(a[u + i],a[v + i]);
}
void dfs(int dep){
if (dep > n){
if (isorder(1,N)) ans += fac[chs];
return;
}
int len = 1 << dep,x = 0,y = 0;
for (int i = 1; i <= N; i += len){
if (!isorder(i,len)){
if (!x) x = i;
else if (!y) y = i;
else return;
}
}
if (!x && !y) dfs(dep + 1);
else if (x && !y){
chs++;
Swap(x,x + (len >> 1),(len >> 1));
dfs(dep + 1);
Swap(x,x + (len >> 1),(len >> 1));
chs--;
}
else if (x && y){
chs++;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++){
Swap(x + i * (len >> 1),y + j * (len >> 1),(len >> 1));
if (isorder(x,len) && isorder(y,len))
dfs(dep + 1);
Swap(x + i * (len >> 1),y + j * (len >> 1),(len >> 1));
}
chs--;
}
}
int main(){
fac[0] = 1;
for (int i = 1; i <= 12; i++) fac[i] = fac[i - 1] * i;
n = read(); N = (1 << n);
REP(i,N) a[i] = read();
dfs(1);
cout << ans << endl;
return 0;
}
BZOJ3990 [SDOI2015]排序 【搜索】的更多相关文章
- [bzoj3990][SDOI2015]排序-搜索
Brief Description 小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1<=i<= ...
- BZOJ 3990: [SDOI2015]排序 [搜索]
3990: [SDOI2015]排序 题意:\(2^n\)的一个排列,给你n种操作,第i种把每\(2^{i-1}\)个数看成一段,交换任意两段.问是这个序列有序的操作方案数,两个操作序列不同,当且仅当 ...
- [BZOJ3990][SDOI2015]排序(DFS)
3990: [SDOI2015]排序 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 902 Solved: 463[Submit][Status][ ...
- [BZOJ3990]:[SDOI2015]排序(搜索)
题目传送门 题目描述 小A有一个1-${2}^{N}$的排列A[1..${2}^{N}$],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1≤i≤N), ...
- Bzoj3990 [SDOI2015]排序
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 651 Solved: 338 Description 小A有一个1-2^N的排列A[1..2^N], ...
- BZOJ 3990 [SDOI2015]排序 ——搜索
[题目分析] 可以发现,操作的先后顺序是不影响结果的,那么答案就是n!的和. 可以从小的步骤开始搜索,使得每一个当前最小的块都是上升的数列,然后看看是否可行即可. 复杂度好像是4^n [代码](哪里写 ...
- BZOJ 3990: [SDOI2015]排序(搜索+剪枝)
[SDOI2015]排序 Description 小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1< ...
- 006-筛选分类排序搜索查找Filter-Classificatio-Sort-Search-Find-Seek-Locate
006-筛选分类排序搜索查找Filter-Classificatio-Sort-Search-Find-Seek-Locate https://www.cnblogs.com/delphixx/p/1 ...
- 【LG3322】[SDOI2015]排序
[LG3322][SDOI2015]排序 题面 洛谷 题解 交换顺序显然不影响答案,所以每种本质不同的方案就给答案贡献次数的阶乘. 从小往大的交换每次至多\(4\)中决策,复杂度\(O(4^n)\). ...
随机推荐
- 2018.4.9 Ubuntu install kreogist-mu
先下载kreogist m文件 然后在下载哪里右键点击打开终端 输入sudo dpkg -i + 文件名 输入密码 下一步会显示 未安装未安装软件包 libmpv1. jiexialai要处理 sud ...
- 2018.3.12 Leecode习题 给定一个整数数列,找出其中和为特定值的那两个数。
给定一个整数数列,找出其中和为特定值的那两个数. 你可以假设每个输入都只会有一种答案,同样的元素不能被重用. 示例: 给定 nums = [2, 7, 11, 15], target = 9; 因为 ...
- mysql 定时任务job
mysql 定时任务job 1.通过show EVENTS显示当前定义的事件 2.检查event_scheduler状态:SHOW VARIABLES LIKE 'event_scheduler' 3 ...
- ★iOS 性能测试工具 SDK
一.概括 1. 做一个类似GT的性能测试工具: 2. 第一期主要是CPU.内存功能,要求可以绘制曲线,可以选择曲线区间,自动计算最小值.最大值.均值等,支持曲线全屏显示 目标的视觉效果是类似股票走势图 ...
- pandas删除及其映射修改操作。
1.使用drop_duplicates()函数删除重复的行 df.drop_duplicates() 2.映射 映射的含义,创建一个映射关系,把values元素和一个特定的标签或字符串绑定 map = ...
- python处理excel总结
工作中,大家经常会使用excel去处理数据以及展示,但是对于部分工作我们可以借助程序帮忙实现,达到高效解决问题的效果,比如将接口返回的json解析并保存结果到excel中,按一定规律处理excel中的 ...
- 忘记root密码怎么办-单用户模式修改root密码
忘记root密码怎么办-单用户模式修改root密码================================= 1,开机3秒内按下向下的方向键,目的是为了不让它进入系统,而是停留在开机界面. 2 ...
- linux文件属性之linux文件删除原理
Linux是通过link的数量来控制文件删除的,只有当一个文件不存在任何link的时候,这个文件才会被删除.一般来说,每个文件都有2个link计数器:i_count和i_nlink. i_count的 ...
- python能干什么?
python能干什么? 网络爬虫 爬虫,指的是从互联网采集数据的程序脚本 . 爬天爬地爬空气 ,无聊的时候爬一爬吃鸡数据.b站评论,能得出很多有意思的结论.知乎有个很有意思的问题——"利用爬 ...
- rs485多主
因复位时I/O口都输出高电平.如果把I/O口直接与RS-485接口芯片的驱动器使能端DE端相连,会在CPU复位其间DE为高,从而使本节点处于发送状态.如果此时总线上其它节点在发送数据,则此次数据传输将 ...