jzoj4049-排序【搜索】

探讨了在长度为2^n的序列中,通过特定操作使序列升序排列的问题。该文介绍了解题思路,即通过递归搜索策略,确保每次操作后的序列部分满足升序条件,最终计算出所有可能的操作序列数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正题

题目链接:https://jzoj.net/senior/#contest/show/3017/0


题目大意

长度为 2 n 2^n 2n的序列, n n n个操作,第 i i i个可以将序列划分为 2 i 2^i 2i段后交换其中两段,每个操作只能用一次,求有多少种操作可以使得序列有小到大。


解题思路

每个操作只能一次操作顺序不会影响结果,所以可以假设从小到大操作,做到第 i i i个操作时,每个可以被交换的段都必须是已经交换好的。

那么假设现在交换的段长度 x x x,那么每段 2 x 2x 2x的最多只有两个不按顺序,否则无解,所以我们搜索一下就好了

时间复杂度 O ( 2 n n ) O(2^nn) O(2nn)


c o d e code code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=14;
ll n,l,ans,a[1<<N],fac[N];
void dfs(ll dep,ll f){
	if(dep>n){
		ans+=fac[f];
		return;
	}
	ll len=1<<dep,h=len/2,z=0,w[4];
	for(ll i=1;i<=l;i+=len){
		if(a[i+h]!=a[i]+h) w[++z]=i;
		if(z>2){return;}
	}
	if(!z)dfs(dep+1,f);
	if(z==1){
		if(a[w[1]]==a[w[1]+h]+h){
			for(ll i=w[1];i<w[1]+h;i++)swap(a[i],a[i+h]);
			dfs(dep+1,f+1);
			for(ll i=w[1];i<w[1]+h;i++)swap(a[i],a[i+h]);
		}
	}
	if(z==2){
		if(a[w[1]]+h==a[w[2]]&&a[w[1]+h]+h==a[w[2]+h]){
			for(ll i=0;i<h;i++)swap(a[w[1]+h+i],a[w[2]+i]);
			dfs(dep+1,f+1);
			for(ll i=0;i<h;i++)swap(a[w[1]+h+i],a[w[2]+i]);
		}
		if(a[w[2]]+h==a[w[1]+h]&&a[w[1]]+h==a[w[2]+h]){
			for(ll i=0;i<h;i++)swap(a[w[1]+i],a[w[2]+i]);
			dfs(dep+1,f+1);
			for(ll i=0;i<h;i++)swap(a[w[1]+i],a[w[2]+i]);
		}
		if(a[w[1]]+h==a[w[2]+h]&&a[w[2]]+h==a[w[1]+h]){
			for(ll i=0;i<h;i++)swap(a[w[1]+h+i],a[w[2]+h+i]);
			dfs(dep+1,f+1);
			for(ll i=0;i<h;i++)swap(a[w[1]+h+i],a[w[2]+h+i]);
		}
		if(a[w[2]+h]+h==a[w[1]+h]&&a[w[2]]+h==a[w[1]]){
			for(ll i=0;i<h;i++)swap(a[w[1]+i],a[w[2]+h+i]);
			dfs(dep+1,f+1);
			for(ll i=0;i<h;i++)swap(a[w[1]+i],a[w[2]+h+i]);
		}
	}
}
int main()
{
	scanf("%lld",&n);
	l=1<<n;fac[0]=1;
	for(ll i=1;i<=n;i++)
		fac[i]=fac[i-1]*i;
	for(ll i=1;i<=l;i++)
		scanf("%lld",&a[i]);
	dfs(1,0);
	printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值