[xsy 2669][树状数组][组合数]归并排序

题目描述
  有一个长度为n的排列 n = 2 k n=2^k n=2k,你要把这个数组归并排序。但是在长度为2的时候有 1 2 \frac{1}{2} 21的概率会把两个数交换(就是有 1 2 \frac{1}{2} 21的概率返回错的结果)。有两种操作
  1:交换两个数
  2:询问排序后的一个位置等于一个数的概率。
k ⩽ 16 , q ⩽ 100000 k\leqslant16,q\leqslant 100000 k16,q100000

solution
考虑相邻的两个数a,b( a &lt; b a&lt;b a<b)
若交换后a排在b后面,则a在接下来的归并排序可以视为b+0.5
即a会始终"粘"着b
即a可能的"值"为 a , b + 0.5 a,b+0.5 a,b+0.5
b可能的值为 b , b b,b b,b
每次查询相当于问有多少种取值方式,使得y-1个数小于它
分类讨论一下,树状数组+组合数即可

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int Mod=1000000007;
int n,q;
#define Maxn 100010
int a[Maxn];
int b[Maxn][2];

int Pow[Maxn];
int fact[Maxn],inv[Maxn];
inline int C(int i,int j){return 1ll*fact[i]*inv[i-j]%Mod*inv[j]%Mod;}

struct Bit{
	int sum[Maxn<<1];
	inline int lowbit(int x){return x&(-x);}
	inline void Add(int x,int ad){
		if(!x)return;
		for(int i=x;i<=2*n+1;i+=lowbit(i))sum[i]+=ad;
	}
	inline int Query(int x){
		int ans=0;
		for(int i=x;i;i-=lowbit(i))ans+=sum[i];
		return ans;
	}
}T1,T2;//min max

inline void Modify(int x){
	T1.Add(b[x][0],-1);T2.Add(b[x][1],-1);
	T1.Add(b[x+1][0],-1);T2.Add(b[x+1][1],-1);
	if(a[x]<a[x+1]){b[x][0]=2*a[x];b[x][1]=2*a[x+1]+1;b[x+1][0]=b[x+1][1]=2*a[x+1];}
	else{b[x][0]=b[x][1]=2*a[x];b[x+1][0]=2*a[x+1];b[x+1][1]=2*a[x]+1;}
	T1.Add(b[x][0],1);T2.Add(b[x][1],1);
	T1.Add(b[x+1][0],1);T2.Add(b[x+1][1],1);
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);
	for(register int i=1;i<=n;++i)rd(a[i]);
	if(n==1){
		rd(q);
		while(q--)puts("1");
		return 0;
	}
	for(register int i=1;i<n;i+=2)Modify(i);
	int opt,x,y;
    fact[0]=1;
    for(register int i=1;i<=n;++i)fact[i]=1ll*fact[i-1]*i%Mod;
    inv[0]=inv[1]=1;
    for(register int i=2;i<=n;++i)inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
    for(register int i=2;i<=n;++i)inv[i]=1ll*inv[i-1]*inv[i]%Mod;
    Pow[0]=1;
    for(register int i=1;i<=n;++i)Pow[i]=1ll*Pow[i-1]*inv[2]%Mod;
    rd(q);
    while(q--){
    	rd(opt);rd(x);rd(y);
    	if(opt==1){
    		swap(a[x],a[y]);
    		if(x&1)Modify(x);
    		else Modify(x-1);
    		if(y&1)Modify(y);
    		else Modify(y-1);
    	}else{
    		int Ans=0;
    	    int ans1=T1.Query(b[x][0]-1);
    	    int ans2=T2.Query(b[x][0]-1);
    	    if(ans2<y&&ans1>=y-1)Ans=(Ans+1ll*C(ans1-ans2,y-1-ans2)*Pow[ans1-ans2+1])%Mod;
    	    ans1=T1.Query(b[x][1]-1);if(b[x][1]>b[x][0])ans1--;
    	    ans2=T2.Query(b[x][1]-1);
    	    if(ans2<y&&ans1>=y-1)Ans=(Ans+1ll*C(ans1-ans2,y-1-ans2)*Pow[ans1-ans2+1])%Mod;
    	    printf("%d\n",Ans);
    	}
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值