[JZOJ5171]归并排序

来源:互联网 发布:发货找车用什么软件 编辑:程序博客网 时间:2024/06/10 22:43

题目大意

给定一个n的排列{Pn},保证n2的整数次幂。有q次操作,分以下两种:
 交换PxPy
 对这个排列进行一个有bug的归并排序:在分治到长度为2时会以1/2的概率选择是否交换两个数。然后求Px排完序之后排名为y的概率是多少。
答案对109+7取模。

1n216,1q105


题目分析

观察发现,出现bug的时候,较小的那个数在排完序之后一定在较大那个数的后一位置。因此我们将出现bug的情况看成较小的数变成较大的数+0.5显然是等价的。
然后问题就可以变成n个数,每个数有两种等概率的取值,数与数之间取值互相独立,并且两个不同的数不会有相同的取值,求某个数排完序之后排名为y的概率。
先考虑怎么做询问,我枚举这个数的取值,然后相当于我要求恰好有y1个数排完序小于它。我们可以求出一定小于它的数的个数以及有1/2概率小于它的数的个数,然后组合数算一算。
然后这个个数怎么统计呢?其实就是一个二维数点的问题,因为要支持修改,因此我们可以考虑使用嵌套数据结构或者定期重构的KD-tree来解决,时间复杂度在O(nlog2n)O(nn)左右,不是特别优秀。
怎么优化时间呢?可以发现这题的点有特殊性质,就是它x坐标一定小于等于y坐标。这样我们可以把它转化为一维上的数区间问题,补集转化一下就可以使用树状数组在O(nlogn)的时间复杂度解决。


代码实现

#include <algorithm>#include <iostream>#include <cstdio>#include <cctype>using namespace std;int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}int buf[30];void write(int x){    if (x<0) putchar('-'),x=-x;    for (;x;x/=10) buf[++buf[0]]=x%10;    if (!buf[0]) buf[++buf[0]]=0;    for (;buf[0];putchar('0'+buf[buf[0]--]));}const int itwo=500000004;const int P=1000000007;const int N=1<<18;int lowbit(int x){return x&-x;}int quick_power(int x,int y){    int ret=1;    for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;    return ret;}int fact[N],invf[N],POW[N],rg[N][2];int a[N];int n,q,d,lim;int C(int n,int m){return 1ll*fact[n]*invf[m]%P*invf[n-m]%P;}struct Fenwick_tree{    int v[N];    void modify(int x,int delta){for (;x<=lim;x+=lowbit(x)) v[x]+=delta;}    int query(int x)    {        int ret=0;        for (;x;x-=lowbit(x)) ret+=v[x];        return ret;    }}t[2];void ADD(int x){t[0].modify(rg[x][0],1),t[1].modify(rg[x][1],1);}void DEL(int x){t[0].modify(rg[x][0],-1),t[1].modify(rg[x][1],-1);}void pre(){    d=quick_power(quick_power(2,n),P-2);    fact[0]=1;    for (int i=1;i<=n;++i) fact[i]=1ll*fact[i-1]*i%P;    POW[0]=1;    for (int i=1;i<=n;++i) POW[i]=1ll*POW[i-1]*itwo%P;    invf[n]=quick_power(fact[n],P-2);    for (int i=n;i>=1;--i) invf[i-1]=1ll*invf[i]*i%P;    for (int i=0;i<n;++i)        if (a[i]>a[i^1]) rg[i][0]=rg[i][1]=(a[i]<<1)-1;        else rg[i][0]=(a[i]<<1)-1,rg[i][1]=a[i^1]<<1;    for (int i=0;i<n;++i) ADD(i);}int calc(int x,int y){    int cnt=t[1].query(x-1),cnt_=t[0].query(x)-1;    return cnt>y?0:1ll*C(cnt_-cnt,y-cnt)*POW[cnt_-cnt]%P;}int main(){    freopen("sort.in","r",stdin),freopen("sort.out","w",stdout);    lim=(n=read())<<1;    for (int i=0;i<n;++i) a[i]=read();    pre();    for (q=read();q--;)    {        int opt=read(),x=read()-1,y=read()-1;        if (opt==1)        {            if (x==y) continue;            DEL(x),DEL(x^1);            if ((x|1)!=(y|1)) DEL(y),DEL(y^1);            swap(a[x],a[y]);            if (a[x]>a[x^1]) rg[x][0]=rg[x][1]=(a[x]<<1)-1,rg[x^1][0]=(a[x^1]<<1)-1,rg[x^1][1]=a[x]<<1;            else rg[x][0]=(a[x]<<1)-1,rg[x][1]=a[x^1]<<1,rg[x^1][0]=rg[x^1][1]=(a[x^1]<<1)-1;            if (x|1!=y|1) if (a[y]>a[y^1]) rg[y][0]=rg[y][1]=(a[y]<<1)-1,rg[y^1][0]=(a[y^1]<<1)-1,rg[y^1][1]=a[y]<<1;            else rg[y][0]=(a[y]<<1)-1,rg[y][1]=a[y^1]<<1,rg[y^1][0]=rg[y^1][1]=(a[y^1]<<1)-1;            ADD(x),ADD(x^1);            if ((x|1)!=(y|1)) ADD(y),ADD(y^1);        }        else        {            int ans=1ll*(calc(rg[x][0],y)+calc(rg[x][1],y))*itwo%P;            write(ans),putchar('\n');        }    }    fclose(stdin),fclose(stdout);    return 0;}
原创粉丝点击