【XSY2669】歸並排序 樹狀數組 簡單組合數學
阿新 • • 發佈:2018-03-06
mat swap signed 概率 %d open sign cti 一個數 。
題目描述
有一個長度為\(n\)的排列\(n=2^k\),你要把這個數組歸並排序。但是在長度為\(2\)的時候有\(\frac{1}{2}\)的概率會把兩個數交換(就是有\(\frac{1}{2}\)的概率返回錯的結果)。有兩種操作
\(1\):交換兩個數
\(2\):詢問排序後的一個位置等於一個數的概率。
\(k\leq 16,q\leq {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\n",ans);
}
}
return 0;
}
【XSY2669】歸並排序 樹狀數組 簡單組合數學