2024.11.14 鲜花

双调排序的正确性证明暨第八交响曲题解

推歌:Double Agent

好多题解都写的或多或少有问题(包括那篇 \(30\) 分钟速通),终于是整明白了。

首先给出双调序列的定义:满足一下条件之一的序列

  1. \(\exists k, \forall i<k,a_i>a_{i+1} \land \forall i>k,a_i<a_{i+1}\) 即是单谷。
  2. 其可以通过循环位移满足 1

注意到根据定义 \(5,6,7,6,5,1,2,3\) 是双调序列,因为其可以通过循环位移成 \(6,5,1,2,3,5,6,7\),这里有很多题解都是错的,大多是说是单峰或单谷,GGrun 有 hack 1,2,8,7,6,5,4,3

upd:这个 hack 是双调,或者说所有的单峰和单谷都是双调,hack 的地方是将它拆开后 1,2,4,3|6,5,8,76,5,8,7 不是单峰或单谷,但它是双调

过程不再解释,但是需要指出的是,双调序列满足从中间分成相等两段后依次经过 Batcher 比较器(即 Compare And Swap 操作)后满足左右两段都是双调序列并且左边所有值都小于右边所有值,并不需要重新构造。

如果这个结论是真的,那么整个算法的正确性是显然的,考虑证明这个结论。

首先应用高德纳的 \(0-1\) 原理,即对于任意排序网络,若其能对 \(0/1\) 序列排序,则其能对所有序列排序。

证明大致就是考虑二进制分解,其对于每一位都能排对。

upd:贺了一个好像更正规的证明

考虑其逆否命题:任何不正确的排序网络都可以构造一种 \(0/1\) 序列使其无法排序,这里给出一个构造。

考虑一个可以使其错误的序列 \(a\),找到其中一个逆序对 \((i,j)\),将 \(\ge a_i\) 的数设为 \(1\),将 \(\le a_j\) 的设为 \(0\)

考虑一对一样的数可以视为交换,也可以视为不交换,所以将他扔进这个排序网络中可以视为其依然只交换原来的交换,\((i,j)\) 依然还是一个逆序对。

考虑一个双调的 \(0/1\) 序列,容易发现其收尾相接一定最多只有连续的一段 \(1\)

对于没有 \(1\)\(0\),算法显然正确。

然后考虑分讨 \(0/1\) 位置和划分区间,下面用 \(00011|10000\) 序列 \(0001110000\) 分成 \(00011\)\(10000\) 两段。

  1. 形如 \(0011111000\) 的序列,即 \(1\) 是连续一段:

    • \(00001|11000\) 即划分到 \(1\)\(1\) 中间:

      容易发现其左边一定是前缀,右面一定是后缀,所以若两边的按位与不为 \(0\),则经过交换后右边一定是连续的一段 \(1\),左边经过将一定后缀变成 \(0\) 也一定最多存在一段连续的 \(1\),满足定义。

      若按位或是 \(0\),其左边一定是 \(0\),右边一定是一段前缀加一段后缀。

    • \(00110|00000\) 即没有划分到 \(1\)\(1\) 中间:

      容易发现其经过交换后右边一定是连续的一段 \(1\),左边一定全是 \(0\),满足定义。

类似分讨 \(11100011\),即是前后缀的划分点,容易证明其算法的正确性。

Code
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
	FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
	FILE *InFile=freopen("symphony.in","r",stdin),*OutFile=freopen("symphony.out","w",stdout);
#endif

const int N=203;
int n,md;

struct V{int l,r,d; V(int a,int b,int c):l(a),r(b),d(c){}};
vector<V> aas[13];

void Srt(int l,int r,int d,vector<V> &c){
	if(l==r) return ;
	else{
		int mid=l+r>>1;
		for(int a=l,b=mid+1;a<=mid&&b<=r;++a,++b) c.emplace_back(a,b,d);
		Srt(l,mid,d+1,c),Srt(mid+1,r,d+1,c);
	}
}
void PSrt(int l,int r,int d,vector<V> &c){
	int mid=l+r>>1;
	for(int a=mid,b=mid+1;b<=r&&a>=l;--a,++b) c.emplace_back(a,b,d);
	Srt(l,mid,d+1,c),Srt(mid+1,r,d+1,c);
}

void Slv(int l,int r,int d){
	if(l==r) return ;
	else{
		int mid=l+r>>1; md=max(md,d);
		Slv(l,mid,d+1),Slv(mid+1,r,d+1);
		PSrt(l,r,0,aas[d]);
	}
}

int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n; int r=1; while(r<n) r<<=1;
	Slv(1,r,1);
	int cnt=0;
	for(int i=md;i;--i){
		sort(aas[i].begin(),aas[i].end(),[](const V &a,const V &b){return a.d<b.d;});
		int la=0;
		for(auto k:aas[i]) if(k.r<=n) if(la!=k.d) ++cnt,la=k.d;
		++cnt;
	}
	cout<<cnt<<endl;
	for(int i=md;i;--i){
		int la=0;
		for(auto k:aas[i]) if(k.r<=n){
			if(la!=k.d) cout<<endl,la=k.d;
			cout<<"CMPSWP R"<<k.l<<" R"<<k.r<<' ';
		}
		cout<<endl;
	}
}
P


posted @ 2024-11-14 20:44  xrlong  阅读(85)  评论(10编辑  收藏  举报