Educational Codeforces Round 57 (Rated for Div. 2) F. Inversion Expectation 概率+逆序对

10 篇文章 0 订阅

题目链接:http://codeforces.com/contest/1096/problem/F

 

题意:给你一个n个数的排列,但是在这个排列中有一些数字被-1覆盖。问你这个排列中逆序对的期望是多少。

 

自己想也只能想到一个部分,还是去网上看了人家的题解做出来的,其实也不是特别难,缺少想法吧。

做法:

       这个逆序对的个数由四个部分组成,假设我们有m个未知数C_{m}^{2}

       1.已知数和已知数之间的逆序对。

       有板子,也可以用树状数组直接做,每次在找到一个数之后去统计有多少个比它大的数字已经出现,然后把这个数也加紧sum数组;

       2.未知数和未知数之间的逆序对。

       因为每一对数字之间的逆序对期望为0.5,又随意取出一对数有C_{2}^{m},也就是m*(m-1)/2种,所以这个数量就是m*(m-1)/4种;

       3.未知数在前,已知数在后的逆序对。

       假设我们已经统计好了对于一个数x,有have[x]个小于它的数(因为是排列,所以已经出现过的数不可能再出现,所以没有等于的情况)。并且我们也统计了对于第i位的数,前面有k个空格,那么对于第i位的已知数来说,这个情况产生的逆序对就是\frac{(m-1)!*k*(m-have[a[i]])}{m!}=\frac{k*(m-have[a[i]])}{m},因为对于前面的k个空格,每个都可以填入(m-have[a[i]])中的一个数,其他的数字是全排列,分母就是所有数的全排列。

       4.已知数在前,未知数在后的逆序对。

       思路同情况3,我们在知道了前面有k个空格之后,那么后面就有(m-k)个空格,得到有have[a[i]]个数比第i位上的数小之后,这个情况产生的逆序对就是\frac{(m-1)!*(m-k)*have[a[i]]}{m!}=\frac{(m-k)*have[a[i]]}{m}

 


代码如下。

#include <bits/stdc++.h>
#define debug printf("####\n")
#define DEBU
using namespace std;
typedef long long ll;
const int maxn=200055;
const ll mod=998244353;
int sum[maxn+10];
ll quick(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return ans;
}
ll inv(ll x){
    return quick(x,mod-2);
}
int lowbit(int x){
    return x&(-x);
}
void add(int x){
    while(x<maxn){
        sum[x]++;
        x+=lowbit(x);
    }
}
ll query(int x){
    ll ans=0;
    while(x){
        ans+=(ll)sum[x];
        x-=lowbit(x);
    }
    return ans;
}
int n;
bool vis[maxn];
ll ty1,ty2,ty3,a[maxn],hav[maxn],m;
//ty1为情况1,ty2为情况2,ty3则为两种情况的和
int main(){
    scanf("%d",&n);
    ll inv4=inv(4);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        if(a[i]!=-1){
            ty1=(ty1+query(maxn)-query(a[i]))%mod;
            vis[a[i]]=1;
            add(a[i]);
        }
        else m++;
    }
    ty2=m*inv4%mod*(m-1)%mod;
    int now=0; ll invm=inv(m);
    for(int i=1;i<=n;i++){
        if(!vis[i]) now++;
        else hav[i]=now;
    }
    ll kong=0;
    for(int i=1;i<=n;i++){
        if(a[i]!=-1){
            ty3=(ty3+hav[a[i]]*(m-kong)%mod*invm%mod+(m-hav[a[i]])*kong%mod*invm%mod)%mod;
        }
        else kong++;
    }
    printf("%lld\n",(ty1+ty2+ty3)%mod);
    return 0;
}

       


 

顺便贴一个n个数的数列逆序对个数计算的板子

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=100005;
int a[maxn],b[maxn],n;
long long ans;
void mm(int st,int mid,int en){
	int i=st,k=st,j=mid+1;
	while(i<=mid&&j<=en){
		if(a[i]<=a[j]){
			b[k++]=a[i++];
		}
		else {
			ans+=j-k;
			b[k++]=a[j++];
		}
	}
	while(i<=mid) b[k++]=a[i++];
	while(j<=en)  b[k++]=a[j++];
	for(i=st;i<=en;i++)
		a[i]=b[i];
}
void ms(int st,int en){
	if(st<en){
		int mid=st+en>>1;
		ms(st,mid);
		ms(mid+1,en);
		mm(st,mid,en); 
	}
}
int main(){
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	ms(1,n);
	printf("%lld\n",ans);
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值