Processing math: 100%

【codeforces 749E】 Inversions After Shuffle

http://codeforces.com/problemset/problem/749/E (题目链接)

题意

  给出一个1~n的排列,从中等概率的选取一个连续段,设其长度为l。对连续段重新进行等概率的全排列,求排列后整个原序列的逆序对的期望个数。

Solution

  考虑对于每一对数(ai,aj),i<j算贡献。


   1.连续段包含ai,aj

  不妨设ai<aj,则只有当排列后ajai前面才会对答案有贡献(ai>aj的情况同理),连续段长度为l

  于是满足aiaj前面的排列数为Pl2l,概率:Pl2lPll=12

  满足包含aiaj的连续段有i(nj+1)个,其概率为:2i(nj+1)n(n+1)

  所以其期望等于两个概率相乘:

  qi,j=i(nj+1)n(n+1)

  这是O(n2)的,考虑优化。总期望:

  Q=ni=1nj=i+1qi,j

  Q=ni=1nj=i+1i(nj+1)n(n+1)

  发现(nj+1)是连续的,于是就变成了:

  Q=ni=1i(ni)(ni+1)2n(n+1)

  这样复杂度就是O(n)的了。


  2.连续段不同时包含ai,aj

  如果ai<aj,那么因为不被连续段同时包含,它们不会有机会改变相对位置,所以不会对答案做出贡献。考虑ai>aj的情况。

  那么连续段可能取到的区间有:[1,j1],[i+1,n]。考虑到区间[i+1,j1]被算了2次,容斥一下,所以区间的概率:

  Pi,j=(j1)j+(ni)(ni+1)(ji1)(ji)n(n+1)

  Pi,j=(n2+n)(2i+2ni)+2ijn(n+1)

  这个Pi,j怎么快速求解呢,考虑逆序对这个东西。

  Q=ni=1nj=i+1(n2+n)(2i+2ni)+2ijn(n+1)

  设满足aj<ai,j>iaj的个数为x,显然x我们可以通过树状数组用求逆序对的方法O(nlogn)的求出来,则:

  Q=ni=1x((n2+n)(2i+2ni))+nj=i+12ijn(n+1)

  那么现在nj=i+12ij怎么求呢。把2i提出去,那么就成了2inj=i+1j我们用y记录满足aj<ai,j>iaj的位置的和,也就是nj=i+1j,那么显然这个东西我们也是可以通过树状数组用求逆序对的方法O(nlogn)的算出来的。则:

  Q=ni=1x((n2+n)(2i+2ni))+2iyn(n+1)

  于是问题就O(nlogn)的解决了。

细节

  mdzz不晓得哪里爆掉了还是精度问题,调了2天,最后莫名AC。。。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// codeforces 749E
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
 
const int maxn=100010;
LL c[maxn],s[maxn],n;
int a[maxn];
long double ans;
 
int lowbit(int x) {
    return x&-x;
}
void add(LL *c,int x,LL val) {
    for (int i=x;i<=n;i+=lowbit(i)) c[i]+=val;
}
LL query(LL *c,int x) {
    LL res=0;
    for (int i=x;i;i-=lowbit(i)) res+=c[i];
    return res;
}
 
void solve1() {   //区间包含
    long double Q=0;
    for (LL i=1;i<=n;i++)
        Q+=(long double)(i*(n-i)*(n-i+1))/2/n/(n+1);
    ans+=Q;
}
void solve2() {   //区间不包含
    long double Q=0;
    for (int i=n;i>=1;i--) {
        LL x=query(c,a[i]-1);
        Q-=(long double)(x*((2*i+2*n*i)-(n*n+n)))/n/(n+1);
        Q+=(long double)(2*i)/n/(n+1)*query(s,a[i]-1);
        add(c,a[i],1);
        add(s,a[i],i);
    }
    ans+=Q;
}
int main() {
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    solve1();
    solve2();
    printf("%.20Lf",ans);
    return 0;
}

贴一个暴力,供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// codeforces 749E
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
  
const int maxn=100010;
LL c[maxn],s[maxn],n;
int a[maxn];
long double ans;
  
int main() {
    freopen("aaa.in","r",stdin);freopen("ccc.out","w",stdout);
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (LL i=1;i<=n;i++)
        ans+=(long double)(i*(n-i)*(n-i+1))/(2*n*(n+1));
    long double res=0;
    for (LL i=n;i>=1;i--) {
        for (LL j=i+1;j<=n;j++)
            if (a[i]>a[j]) res+=(long double)((j-1)*j+(n-i)*(n-i+1)-(j-i-1)*(j-i))/(n*(n+1));
    }
    ans+=res;
    printf("%.20Lf",ans);
    return 0;
}

 

posted @   MashiroSky  阅读(849)  评论(2编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥
点击右上角即可分享
微信分享提示