【codeforces 749E】 Inversions After Shuffle
http://codeforces.com/problemset/problem/749/E?(題目鏈接)
題意
給出一個1~n的排列,從中等概率的選取一個連續段,設其長度為l。對連續段重新進行等概率的全排列,求排列后整個原序列的逆序對的期望個數。
Solution
考慮對于每一對數${(a_i,a_j),i<j}$算貢獻。
? 1.連續段包含${a_i,a_j}$
不妨設${a_i<a_j}$,則只有當排列后${a_j}$再${a_i}$前面才會對答案有貢獻(${a_i>a_j}$的情況同理),連續段長度為${l}$。
于是滿足${a_i}$在${a_j}$前面的排列數為${P_l^{l-2}}$,概率:${\frac{P_l^{l-2}}{P_l^l}=\frac{1}{2}}$。
滿足包含${a_i}$和${a_j}$的連續段有${i*(n-j+1)}$個,其概率為:${\frac{2*i*(n-j+1)}{n*(n+1)}}$。
所以其期望等于兩個概率相乘:
$${q_{i,j}=\frac{i*(n-j+1)}{n*(n+1)}}$$
這是${O(n^2)}$的,考慮優化。總期望:
$${Q=\sum_{i=1}^n ?\sum_{j=i+1}^n ?q_{i,j}}$$
$${Q=\sum_{i=1}^n ?\sum_{j=i+1}^n ?\frac{i*(n-j+1)}{n*(n+1)}}$$
發現${(n-j+1)}$是連續的,于是就變成了:
$${Q=\sum_{i=1}^n ?\frac {i*(n-i)*(n-i+1)} {2*n*(n+1)}}$$
這樣復雜度就是${O(n)}$的了。
2.連續段不同時包含${a_i,a_j}$
如果${a_i<a_j}$,那么因為不被連續段同時包含,它們不會有機會改變相對位置,所以不會對答案做出貢獻。考慮${a_i>a_j}$的情況。
那么連續段可能取到的區間有:${[1,j-1],[i+1,n]}$。考慮到區間${[i+1,j-1]}$被算了2次,容斥一下,所以區間的概率:
$${P_{i,j}=\frac ?{(j-1)*j+(n-i)*(n-i+1)-(j-i-1)*(j-i)} ?{n*(n+1)}}$$
$${P_{i,j}=\frac ?{(n^2+n)-(2*i+2*n*i)+2*i*j} ?{n*(n+1)}}$$
這個${P_{i,j}}$怎么快速求解呢,考慮逆序對這個東西。
$${Q=\sum_{i=1}^n ?\sum_{j=i+1}^n ?\frac ?{(n^2+n)-(2*i+2*n*i)+2*i*j} ?{n*(n+1)}}$$
設滿足${a_j<a_i,j>i}$的${a_j}$的個數為${x}$,顯然${x}$我們可以通過樹狀數組用求逆序對的方法${O(nlogn)}$的求出來,則:
$${Q=\sum_{i=1}^n ?\frac ?{x*((n^2+n)-(2*i+2*n*i)) ?+ ?\sum_{j=i+1}^n 2*i*j} ?{n*(n+1)}}$$
那么現在${\sum_{j=i+1}^n 2*i*j}$怎么求呢。把${2*i}$提出去,那么就成了${2*i*\sum_{j=i+1}^n j}$我們用${y}$記錄滿足${a_j<a_i,j>i}$的${a_j}$的位置的和,也就是${\sum_{j=i+1}^n j}$,那么顯然這個東西我們也是可以通過樹狀數組用求逆序對的方法${O(nlogn)}$的算出來的。則:
$${Q=\sum_{i=1}^n ?\frac ?{x*((n^2+n)-(2*i+2*n*i)) ?+ ?2*i*y} ?{n*(n+1)}}$$
于是問題就${O(nlogn)}$的解決了。
細節
mdzz不曉得哪里爆掉了還是精度問題,調了2天,最后莫名AC。。。
代碼
// 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; }貼一個暴力,供參考:
// 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; }?
轉載于:https://www.cnblogs.com/MashiroSky/p/6246624.html
總結
以上是生活随笔為你收集整理的【codeforces 749E】 Inversions After Shuffle的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 号码检测,节省成本利器
- 下一篇: 单片机阵列式键盘实验C语言,单片机4×4