CF749E Inversions After Shuffle

2020-01-14 21:39发布

Link
我们可以把贡献拆成两部分计算。
对于一对\(a_i,a_j(i<j)\),如果我们重排的区间\([l,r]\)满足\([i,j]\subseteq[l,r]\),那么不论\(a_i,a_j\)的关系如何,它们都有\(\frac12\)的概率产生\(1\)的贡献。
这里的总的贡献是\(\frac{\sum\limits_{i=1}^n\sum\limits_{j=i+1}^ni(n-j+1)}{n(n+1)}=\frac{\sum\limits_{i=1}^ni(n-i)(n-i+1)}{n(n+1)}\)
其实可以推出\(O(1)\)的式子,不过没有必要了。
对于一对\(a_i,a_j(i<j\wedge a_i>a_j)\),如果我们重排的区间\([l,r]\)满足\([i,j]\not\subseteq[l,r]\),那么它们就会产生\(1\)的贡献。
这里总的贡献是\(\frac{\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n[a_i>a_j][n(n+1)-i(n-j+1)]}{n(n+1)}=\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n[a_i>a_j]-\frac{\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n[a_i>a_j]i(n-j+1)}{n(n+1)}\)
树状数组维护一下就好了。

#include<cstdio>
#include<cctype>
const int N=100007;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
using db=double;
using ll=long long;
int n;
struct bit
{
    ll t[N];
    void add(int p,int v){for(;p;p^=p&-p)t[p]+=v;}
    ll ask(int p){ll s=0;for(;p<=n;p+=p&-p)s+=t[p];return s;}
}t1,t2;
int main()
{
    n=read();db ans=0,Ans=0;
    for(int i=1,a;i<=n;++i) a=read(),Ans+=t1.ask(a+1),ans+=(db)(n-i+1)*i*(n-i)/4.0-(db)t2.ask(a+1)*(n-i+1),t1.add(a,1),t2.add(a,i);
    printf("%.10lf",Ans+ans/(1ll*n*(n+1)>>1));
}
标签: