【XSY2669】归并排序 树状数组 简单组合数学

题目描述

  有一个长度为(n)的排列(n=2^k),你要把这个数组归并排序。但是在长度为(2)的时候有(frac{1}{2})的概率会把两个数交换(就是有(frac{1}{2})的概率返回错的结果)。有两种操作

  (1):交换两个数

  (2):询问排序后的一个位置等于一个数的概率。

  (kleq 16,qleq {10}^5)

题解

  这个排序有点奇怪。两个数(a,b(a<b))排序后可能是(ab)也可能是(ba)

  观察到(ab)会正常排序,而(ba)(a)会一直跟着(b),所以可以把(a)看成(b+0.5)

  这样就会有一些数确定,有些数是两个数中的一个。

  用两个树状数组维护小的数和大的数不超过(x)的个数,每次询问用组合数乱搞即可。

  时间复杂度:(O((n+q)log n))

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
	if(a>b)
		swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
	char str[100];
	sprintf(str,"%s.in",s);
	freopen(str,"r",stdin);
	sprintf(str,"%s.out",s);
	freopen(str,"w",stdout);
#endif
}
int rd()
{
	int s=0,c;
	while((c=getchar())<'0'||c>'9');
	do
	{
		s=s*10+c-'0';
	}
	while((c=getchar())>='0'&&c<='9');
	return s;
}
void put(int x)
{
	if(!x)
	{
		putchar('0');
		return;
	}
	static int c[20];
	int t=0;
	while(x)
	{
		c[++t]=x%10;
		x/=10;
	}
	while(t)
		putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
	if(b<a)
	{
		a=b;
		return 1;
	}
	return 0;
}
int upmax(int &a,int b)
{
	if(b>a)
	{
		a=b;
		return 1;
	}
	return 0;
}
const ll p=1000000007;
ll fp(ll a,ll b)
{
	ll s=1;
	for(;b;b>>=1,a=a*a%p)
		if(b&1)
			s=s*a%p;
	return s;
}
int n,a[100010];
int c1[200010];
int c2[200010];
void add(int *c,int x,int v)
{
	for(;x<=2*n;x+=x&-x)
		c[x]+=v;
}
int sum(int *c,int x)
{
	int s=0;
	for(;x;x-=x&-x)
		s+=c[x];
	return s;
}
int a1[100010];
int a2[100010];
int o(int x)
{
	return ((x-1)^1)+1;
}
void add(int x,int v)
{
	int y=o(x);
	if(a[x]>a[y])
	{
		add(c1,2*a[x]-1,v);
		add(c2,2*a[x]-1,v);
		a1[x]=a2[x]=2*a[x]-1;
	}
	else
	{
		add(c1,2*a[x]-1,v);
		add(c2,2*a[y],v);
		a1[x]=2*a[x]-1;
		a2[x]=2*a[y];
	}
}
ll inv[100010];
ll fac[100010];
ll ifac[100010];
ll c(int x,int y)
{
	if(x<y)
		return 0;
	if(y<0)
		return 0;
	return fac[x]*ifac[y]%p*ifac[x-y]%p;
}
ll query(int x,int y,int b=0)
{
	ll ans=1;
	x--;
	y--;
	int s1=sum(c2,y);
	int s2=sum(c1,y)-s1;
	if(b)
		s2--;
	ans=ans*c(s2,x-s1)%p*fp(inv[2],s2)%p;
	return ans;
}
int main()
{
	open("c");
	scanf("%d",&n);
	int i;
	inv[0]=inv[1]=fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(i=2;i<=n;i++)
	{
		inv[i]=-p/i*inv[p%i]%p;
		fac[i]=fac[i-1]*i%p;
		ifac[i]=ifac[i-1]*inv[i]%p;
	}
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
		add(i,1);
	int q;
	scanf("%d",&q);
	int op,x,y;
	for(i=1;i<=q;i++)
	{
		scanf("%d%d%d",&op,&x,&y);
		if(op==1)
		{
			add(x,-1);
			add(y,-1);
			add(o(x),-1);
			add(o(y),-1);
			swap(a[x],a[y]);
			add(x,1);
			add(y,1);
			add(o(x),1);
			add(o(y),1);
		}
		else
		{
			ll ans;
			if(a1[x]==a2[x])
				ans=query(y,a1[x]);
			else
				ans=(query(y,a1[x])+query(y,a2[x],1))*inv[2]%p;
			ans=(ans+p)%p;
			printf("%lld
",ans);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8513605.html