1. 程式人生 > >【XSY2669】歸並排序 樹狀數組 簡單組合數學

【XSY2669】歸並排序 樹狀數組 簡單組合數學

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】歸並排序 樹狀數組 簡單組合數學