归并排序 [20220210模拟赛] 归并排序/组合数学/树状数组

题意

给定长为 \(2^k\) 的排列 \(p\),你要对这个排列进行归并排序。但当当前序列长度为 \(2\) 的时候,有 \(\dfrac{1}{2}\) 的概率会把两个数的位置交换,\(\dfrac{1}{2}\) 的概率原封不动。现在有 \(q\) 次操作:

  • 1 x y:交换 \(p_x,p_y\)
  • 2 x y:查询排完序后的序列 \(p'\)\(p'_y=p_x\) 的概率

题解

我们考虑分别在 \(2i-1\)\(2i\) 位置的两个数 \(x,y\),那么底层递归的时候我们就会对 \(x,y\) 进行随机交换。

不妨设 \(x<y\)

如果交换后 \(x\) 仍然在 \(y\) 前面那自然啥事没有。如果 \(x\)\(y\) 后面,我们考虑归并的过程:

\[y\ ,x\ ,\cdots\\ a\ ,b\ ,\cdots \]

不妨设 \(a>y\),那么 \(a>y>x\)

进行归并时,先比较 \(a,y\)。发现 \(y\) 比较小,先将 \(y\) 归并到新数组内;接着比较 \(a,x\),发现 \(x\) 比较小,因此紧接着就会将 \(x\) 归并到新数组内。推广一下就是,如果相邻两数 \(x,y\) 在交换后不满足升序,那么 \(x,y\) 无论如何都是相邻的,且 \(x\)\(y\) 后面。

如何让排完序后 \(x,y\) 一直相邻?原本排完序后 \(y\) 后面应该是 \(y+1\),但现在我们要让它是 \(x\),那么很简单,把 \(x\) 看做一个 \(y\)\(y+1\) 之间的数就行了。

这样一来,对每个 \(p_{2i-1}\)\(p_{2i}\),设其中较小的为 \(x\),较大的为 \(y\),我们赋予 \(x\) 两种权值:一种就是 \(x\),一种是 \(y\)\(y+1\) 之间的一个权值。\(y\) 自然只有一种取值,就是 \(y\)

接下来考虑我们的询问:\(p_a\) 排完序后应当在 \(b\) 的位置。 这实际上就是说,对于每个数,我们需要选取它的一种可能的取值,使得 \(\le p_a\) 的数恰有 \(b\) 个。

对每个数 \(p_i\),我们记 \(u_i\) 为其较小的可能值,\(w_i\) 为其较大的可能值,则有 \(u_i\le w_i\)。如果 \(p_i\) 只有一种取值,我们认为 \(u_i=w_i=p_i\)

根据 \(u_i,w_i\)\(p_a\) 的大小关系,我们将所有数分为三类:

  • \(p_a< u_i< w_i\):这种数必然会放在 \(p_a\) 后面,可以直接不管它们。
  • \(u_i< w_i< p_i\):这种数必然会放在 \(p_a\) 前面。设这种数的数量为 \(r_2\)
  • \(u_i< p_i< w_i\):如果选了 \(u_i\),那么就会放在 \(p_a\) 前面;否则就会放在后面。设这种数的数量为 \(r_1\),那么可以发现此时 \(p_a\) 前面还需要 \(b-1-r_2\) 个数,因此我们要做的实际上就是从这 \(r_1\) 个数中选出 \(b-1-r_2\) 个数,让这些数选取 \(u_i\),补上前面的空缺;剩下的选择 \(w_i\),放在后面。那么方案数正是你所熟悉的组合数:\(C_{r_1}^{b-1-r_2}\)

现在我们只需要求出 \(r_1\)\(r_2\) 即可。

使用两个值域上的树状数组维护 \(\{u_n\},\{w_n\}\)\(< p_a\) 的数的个数,如果 \(\{w_n\}\)\(<p_a\) 的数有 \(c_1\) 个,\(\{u_n\}\)\(< p_a\) 的数有 \(c_2\) 个,敏锐的你或许早已注意到,\(r_1\) 的值正是 \(c_1\), 而 \(r_2\) 则等于 \(c_2-c_1\)

原题还需要进行修改:交换两个数的位置。由于涉及的修改量只有 \(O(1)\) 个,我们可以直接计算产生的贡献差,更新树状数组即可。

需要注意的是,\(p_a\) 本身就可能有两种取值,我们需要根据 \(p_a\) 到底有几种取值进行分类讨论。

总的时间复杂度为 \(O((n+q)\log n)\)

#include<bits/stdc++.h>

#define int long long

using namespace std;

inline int read(){
	int x=0,f=1;char c=getchar();
	for(;(c<'0'||c>'9');c=getchar()){if(c=='-')f=-1;}
	for(;(c>='0'&&c<='9');c=getchar())x=x*10+(c&15);
	return x*f;
}

const int MN=1e5+5;

int n,q;
int p[MN];

struct BIT{
	int c[MN<<1];
	int lowbit(int x){return x&(-x);}
	void add(int x,int k){for(int i=x;i<=2*n+1;i+=lowbit(i))c[i]+=k;}
	int query(int x){int res=0;for(int i=x;i;i-=lowbit(i))res+=c[i];return res;}
}S,T;

void del(int id){
	if(id%2==0)id--;
	int x=p[id],y=p[id+1];
	if(x>y)swap(x,y);
	T.add(2*x,-1),S.add(2*y+1,-1);
	T.add(2*y,-1),S.add(2*y,-1);
}

void ins(int id){
	if(id%2==0)id--;
	int x=p[id],y=p[id+1];
	if(x>y)swap(x,y);
	T.add(2*x,1),S.add(2*y+1,1);
	T.add(2*y,1),S.add(2*y,1);
}

int ifac[MN],fac[MN];

const int mod=1e9+7;

int ksm(int x,int y,int p=mod){
	int res=1;
	for(int i=y;i;i>>=1,x=x*x%p)if(i&1)res=res*x%p;
	return res;
}

int inv(int x,int p=mod){return ksm(x,p-2,p)%p;}

void init(){
	fac[0]=ifac[0]=1;for(int i=1;i<=n;i++)ifac[i]=ifac[i-1]*inv(i)%mod,fac[i]=fac[i-1]*i%mod;;
}

int C(int x,int y){
	if(x<y||y<0)return 0;
	return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}

int valx(int x){
	return 2*p[x];
}

int valy(int x){
	if(x%2==0){
		int a=p[x-1],b=p[x];
		if(a>b)return 2*a+1;
		else return 2*b;
	}
	else{
		int a=p[x+1],b=p[x];
		if(a>b)return 2*a+1;
		else return 2*b;
	}
}

signed main(void){

	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);

	n=read();init();
	for(int i=1;i<=n;i++)p[i]=read();

	for(int i=1;i*2<=n;i++)ins(i*2-1);

	q=read();
	while(q--){
		int op=read(),x=read(),y=read();
		if(op==1){
			del(x),del(y);
			swap(p[x],p[y]);
			ins(x),ins(y);
		}
		else{
			int ans=0;
			if(valx(x)==valy(x)){
				int cnt2=S.query(valx(x)-1),cnt1=T.query(valx(x)-1);
				cout<<C(cnt1-cnt2,y-1-cnt2)%mod*ksm(inv(2),cnt1-cnt2)%mod<<endl;
				continue;
			}
			int cnt2=S.query(valx(x)-1),cnt1=T.query(valx(x)-1);
			ans=(ans+C(cnt1-cnt2,y-1-cnt2)%mod*ksm(inv(2),cnt1-cnt2+1)%mod)%mod;
			cnt2=S.query(valy(x)-1),cnt1=T.query(valy(x)-1)-1;
			ans=(ans+C(cnt1-cnt2,y-1-cnt2)%mod*ksm(inv(2),cnt1-cnt2+1)%mod)%mod;
			cout<<ans<<endl;
		}
	}

	return 0;
}
posted @ 2022-02-11 12:11  云浅知处  阅读(71)  评论(0)    收藏  举报