【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,则只有当排列后aj再ai前面才会对答案有贡献(ai>aj的情况同理),连续段长度为l。
于是满足ai在aj前面的排列数为Pl−2l,概率:Pl−2lPll=12。
满足包含ai和aj的连续段有i∗(n−j+1)个,其概率为:2∗i∗(n−j+1)n∗(n+1)。
所以其期望等于两个概率相乘:
qi,j=i∗(n−j+1)n∗(n+1)
这是O(n2)的,考虑优化。总期望:
Q=n∑i=1n∑j=i+1qi,j
Q=n∑i=1n∑j=i+1i∗(n−j+1)n∗(n+1)
发现(n−j+1)是连续的,于是就变成了:
Q=n∑i=1i∗(n−i)∗(n−i+1)2∗n∗(n+1)
这样复杂度就是O(n)的了。
2.连续段不同时包含ai,aj
如果ai<aj,那么因为不被连续段同时包含,它们不会有机会改变相对位置,所以不会对答案做出贡献。考虑ai>aj的情况。
那么连续段可能取到的区间有:[1,j−1],[i+1,n]。考虑到区间[i+1,j−1]被算了2次,容斥一下,所以区间的概率:
Pi,j=(j−1)∗j+(n−i)∗(n−i+1)−(j−i−1)∗(j−i)n∗(n+1)
Pi,j=(n2+n)−(2∗i+2∗n∗i)+2∗i∗jn∗(n+1)
这个Pi,j怎么快速求解呢,考虑逆序对这个东西。
Q=n∑i=1n∑j=i+1(n2+n)−(2∗i+2∗n∗i)+2∗i∗jn∗(n+1)
设满足aj<ai,j>i的aj的个数为x,显然x我们可以通过树状数组用求逆序对的方法O(nlogn)的求出来,则:
Q=n∑i=1x∗((n2+n)−(2∗i+2∗n∗i))+∑nj=i+12∗i∗jn∗(n+1)
那么现在∑nj=i+12∗i∗j怎么求呢。把2∗i提出去,那么就成了2∗i∗∑nj=i+1j我们用y记录满足aj<ai,j>i的aj的位置的和,也就是∑nj=i+1j,那么显然这个东西我们也是可以通过树状数组用求逆序对的方法O(nlogn)的算出来的。则:
Q=n∑i=1x∗((n2+n)−(2∗i+2∗n∗i))+2∗i∗yn∗(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; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥