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

题目描述

  有一个长度为\(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;
}

原文地址:https://www.cnblogs.com/ywwyww/p/8513605.html

时间: 2024-08-27 13:36:24

【XSY2669】归并排序 树状数组 简单组合数学的相关文章

UVA 11990 `Dynamic&#39;&#39; Inversion CDQ分治, 归并排序, 树状数组, 尺取法, 三偏序统计 难度: 2

题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3141 题意 一个1到n的排列,每次随机删除一个,问删除前的逆序数 思路 综合考虑,对每个数点,令value为值,pos为位置,time为出现时间(总时间-消失时间),明显是统计value1 > value2, pos1 < pos2, time1 < time2的个

代码与算法集锦-归并排序+树状数组+快排+深度优先搜索+01背包(动态规划)

归并排序 求逆序数 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 首先考虑下如何将将二个有序数列合并.这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数.然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可. //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c

nyist oj 117 求逆序数 (归并排序&amp;&amp;树状数组)

求逆序数 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数. 现在,给你一个N个元素的序列,请你判断出它的逆序数是多少. 比如 1 3 2 的逆序数就是1. 输入 第一行输入一个整数T表示测试数据的组数(1<=T<=5) 每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000) 随后的一行共有N个整

Ultra-QuickSort (poj 2299 归并排序 || 树状数组 求逆序对)

Language: Default Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 45751   Accepted: 16615 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct i

1090. In the Army Now (Ural 1090 归并排序||树状数组)

1090. In the Army Now Time limit: 1.0 second Memory limit: 64 MB The sergeant ordered that all the recruits stand in rows. The recruits have formed K rows with Npeople in each, but failed to stand according to their height. The right way to stand in

HDU 1541 Stars 树状数组简单应用

点击打开链接 Stars Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 5140    Accepted Submission(s): 2020 Problem Description Astronomers often examine star maps where stars are represented by points o

洛谷 P1908 逆序对 Label:归并排序||树状数组

题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对.知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目. 输入输出格式 输入格式: 第一行,一个数n,表示序列中有n个数. 第二行n个数,表示给定的序列. 输出格式: 给定序列中逆序对的数目. 输入输出样例 输

Bzoj 1227: [SDOI2009]虔诚的墓主人 树状数组,离散化,组合数学

1227: [SDOI2009]虔诚的墓主人 Time Limit: 5 Sec  Memory Limit: 259 MBSubmit: 895  Solved: 422[Submit][Status][Discuss] Description 小W 是一片新造公墓的管理人.公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地.当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地.为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度

Day2:T4求逆序对(树状数组+归并排序)

T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*(); B[J]>=B[I] 也就是求逆序对: 求逆序对的方法主要有两种: 归并排序: 树状数组: 这里两种方法都学习一下: 1.之前对于树状数组的印象就只有单点修改和区间求和 一直觉得lowbit是一个神奇的东西(至今没有搞懂原理) 上网搜了一下用树状数组求逆序对的方法,发现有一个大神写的很棒....看