洛谷4223 BZOJ5058 期望逆序对 期望 组合数学 矩阵乘法 树状数组

19 篇文章 0 订阅
19 篇文章 0 订阅

题目链接

题意:
给你一个长度为 n n n的排列,有 k k k次操作,每次随机两个不同的位置,交换两个位置的元素,求 k k k次交换后期望的逆序对数。为了避免答案是小数的问题,结果乘上 ( C n 2 ) k (C_{n}^{2})^k (Cn2)k,并且对 1 e 9 + 7 1e9+7 1e9+7取模。 n &lt; = 500000 , k &lt; = 1 e 9 n&lt;=500000,k&lt;=1e9 n<=500000,k<=1e9

题解:
这个题真的是我花了一整天的时间才差不多明白,但是也不敢说理解的非常好。感觉网上的题解都写得太简略,似乎有一篇稍微详细一点的还有一些错误,于是导致我很久都没有明白题解在说什么。幸亏有大佬dsgsjk帮我,让我能够比较明白这个题的做法。于是我来照着我的理解说一下这个题的做法。

我们的一个想法是对每一个位置对 ( A , B ) (A,B) (A,B)来考虑统计答案(假设 A &lt; B A&lt;B A<B),那么我们考虑 k k k次交换后这两个位置上的数会变成原来哪个位置的数。我们其实可以发现,对于每一个不是 A A A也不是 B B B的位置,他们在这里可以认为是等价的,原因是它们当中的每一个换过来之后,在每一种情况中出现的期望次数相同,于是我们在这里一起计算。由于结果乘上 ( C n 2 ) k (C_{n}^{2})^k (Cn2)k,所以我们的期望就变成计算每一种情况出现的总次数了。我们经过分类可以发现,对于 ( A , B ) (A,B) (A,B)这个位置,在交换之后,可能会变成七种情况: ( A , B ) (A,B) (A,B) ( A , C ) (A,C) (A,C) ( B , A ) (B,A) (B,A) ( B , C ) (B,C) (B,C) ( C , A ) (C,A) (C,A) ( C , B ) (C,B) (C,B) ( C , C ) (C,C) (C,C)。我们发现我们的操作次数是 1 e 9 1e9 1e9级别的,所以我们没法去模拟每一次操作可能出现的结果,但是对于这种操作次数比较大的情况,我们通常会考虑使用矩乘来解决,于是我们考虑构造矩阵。

我们第一行的6个元素分别代表 ( A , B ) (A,B) (A,B), ( C , B ) (C,B) (C,B), ( B , A ) (B,A) (B,A), ( C , A ) (C,A) (C,A), ( B , C ) (B,C) (B,C), ( A , C ) (A,C) (A,C), ( C , C ) (C,C) (C,C) k k k次操作后的系数,这个矩阵的推法就是考虑每种情况有多少种方法转移到另一种情况。构造出如下矩阵:
[ C n − 2 2 n − 2 1 0 0 n − 2 0 1 C n − 2 2 + n − 3 0 1 1 0 n − 3 1 0 C n − 2 2 n − 2 n − 2 0 0 0 1 1 C n − 2 2 + n − 3 0 1 n − 3 0 1 1 0 C n − 2 2 + n − 3 1 n − 3 1 0 0 1 1 C n − 2 2 + n − 3 n − 3 0 1 0 1 1 1 C n − 2 2 + 2 ( n − 4 ) + 1 ] \begin{bmatrix} C_{n-2}^2 &amp; n-2 &amp; 1 &amp; 0 &amp; 0 &amp; n-2 &amp; 0 \\ 1 &amp; C_{n-2}^2+n-3 &amp; 0 &amp; 1 &amp; 1 &amp; 0 &amp; n-3 \\ 1 &amp; 0 &amp; C_{n-2}^2 &amp; n-2 &amp; n-2 &amp; 0 &amp; 0 \\ 0 &amp; 1 &amp; 1 &amp; C_{n-2}^2+n-3 &amp; 0 &amp; 1 &amp; n-3 \\ 0 &amp; 1 &amp; 1 &amp; 0 &amp; C_{n-2}^2+n-3 &amp; 1 &amp; n-3 \\ 1 &amp; 0 &amp; 0 &amp; 1 &amp; 1 &amp; C_{n-2}^2+n-3 &amp; n-3 \\ 0 &amp; 1 &amp; 0 &amp; 1 &amp; 1 &amp; 1 &amp; C_{n-2}^2+2(n-4)+1 \\ \end{bmatrix} Cn22110010n2Cn22+n30110110Cn22110001n2Cn22+n301101n20Cn22+n311n20011Cn22+n310n30n3n3n3Cn22+2(n4)+1
我们通过矩阵乘法求出 k k k次操作后每一种情况的系数。我们考虑从 1 1 1 n n n枚举 ( A , B ) (A,B) (A,B)中的 B B B,对于每一个 B B B来算每种情况下的逆序对数的贡献。我们设在原序列中在 B B B之前比 B B B小的数的个数为 a a a;比 B B B小的每一个数,前面的位置数之和为 f a fa fa;比 B B B小的每一个数,后面除去 B B B以外的位置数之和 g a ga ga;在 B B B之前比 B B B大的数的个数为 b b b;比 B B B大的每一个数,前面的位置数之和为 f b fb fb;比 B B B大的每一个数,后面除去 B B B以外的位置数之和 g b gb gb。我们统计个数和与位置数之和的原因是,我们要对于当前 B B B,快速计算出之前所有的 A A A,也就是之前所有枚举过的位置在当前对答案的贡献。

上面的几个量都可以用树状数组来维护,但是我们实际上并不用开6个树状数组,原因是原序列是个排列,所以不是比原序列上当前数大的就是比当前数小的,我们可以统计一下每个量的总个数,只维护 a a a f a fa fa g a ga ga三个变量,用总数减一下就能算出其他的量。这样我们就可以只开3个树状数组来维护 a a a f a fa fa g a ga ga三个变量,每次计算完当前的 i i i时把 i i i这个位置的三个变量更新一下。

我们还是分那七种情况对答案的贡献,其中对于第七种情况 ( C , C ) (C,C) (C,C),我们可以在最后一起算答案,不用每次去单独拿出来算。那么我们考虑其他六种情况对应的贡献。我一种一种来解释一下。首先我们先明确一下,两个数 ( x , y ) (x,y) (x,y)之间形成逆序对的条件是, x x x的位置在 y y y前面并且 x x x y y y大,或者 x x x的位置在 y y y的后面并且 x x x y y y小。我们设矩阵是 x x x,根据之前的说明,我们需要的是矩阵的第一行的结果。我用大写字母来表示位置,也就是下标。
( A , B ) (A,B) (A,B) b ∗ x [ 1 ] [ 1 ] b*x[1][1] bx[1][1]
我们统计一下之前有多少个比 B B B位置上的数大的数,只有比 B B B大并且在 B B B前面的 A A A才会在这种情况下形成逆序对,乘上 ( A , B ) (A,B) (A,B)这种情况出现的次数,就是答案。
( C , B ) (C,B) (C,B) [ a ∗ ( n − B ) + b ∗ ( B − 2 ) ] ∗ x [ 1 ] [ 2 ] ∗ 1 n − 2 [a*(n-B)+b*(B-2)]*x[1][2]*\frac{1}{n-2} [a(nB)+b(B2)]x[1][2]n21
看作把 A A A换走了,那么对于所有比 B B B位置上的数小的 A A A,我们把它放在 B B B后面的每一个位置,都会产生一个逆序对,同理我们把所有比 B B B位置上的数大的 A A A,我们把它放在 B B B的前面的每一个位置,都会产生一个逆序对。前面去掉当前的 B B B C C C之后有 B − 2 B-2 B2个位置,后面有 n − B n-B nB个位置。这里统一解释一下这6种情况中所有含有 C C C的情况都要除以 n − 2 n-2 n2的原因,原因是在矩阵乘法的时候,我们是把所有等价的 C C C的总数一起算了,这里实际上是要具体一个 C C C的总方案数,由于之前说的每一个 C C C是等价的,所以要把矩阵乘法算出来的方案除以 n − 2 n-2 n2
( B , A ) (B,A) (B,A) a ∗ x [ 1 ] [ 3 ] a*x[1][3] ax[1][3]
B B B前面有多少个比 B B B位置上的数小的 A A A,每一个这样的 A A A换到 B B B后面都会产生一个逆序对。
( C , A ) (C,A) (C,A) ( f b + g a ) ∗ x [ 1 ] [ 4 ] ∗ 1 n − 2 (fb+ga)*x[1][4]*\frac{1}{n-2} (fb+ga)x[1][4]n21
看作把 B B B换走了,对于每一个比 B B B大的 A A A,把 B B B换到原来 A A A前面的每一个位置都会产生一个逆序对;对于每一个比 B B B小的 A A A,现在 A A A换到了这里,所以把 B B B换到原来 A A A后面的每一个位置都会有一个逆序对
( B , C ) (B,C) (B,C) [ a ∗ ( B − 2 ) + b ∗ ( n − B ) ] ∗ x [ 1 ] [ 5 ] ∗ 1 n − 2 [a*(B-2)+b*(n-B)]*x[1][5]*\frac{1}{n-2} [a(B2)+b(nB)]x[1][5]n21
( C , B ) (C,B) (C,B)道理差不多。
( A , C ) (A,C) (A,C) ( g b + f a ) ∗ x [ 1 ] [ 6 ] (gb+fa)*x[1][6] (gb+fa)x[1][6]
( C , A ) (C,A) (C,A)差不多。

还剩下一个 ( C , C ) (C,C) (C,C)的情况,式子是 C n 2 ∗ 1 2 ∗ x [ 1 ] [ 7 ] C_{n}^2*\frac{1}{2}*x[1][7] Cn221x[1][7]
这里对所有的 C C C一起算,就不去除 n − 2 n-2 n2了,对于这些 C C C,有 C n 2 C_{n}^2 Cn2种组合,每两种组合只会产生一个逆序对,也就是对于两个 C C C,他们的值分别是 ( x , y ) (x,y) (x,y),那么要么 x &gt; y x&gt;y x>y,要么 y &gt; x y&gt;x y>x

这样就算完答案了。
复杂度 O ( 7 3 l o g k + n l o g n ) O(7^3logk+nlogn) O(73logk+nlogn)

代码:

#include <bits/stdc++.h>
using namespace std;

int n,kk,v[500010];
long long ans[9][9],x[9][9],ni2,ni,fz[9][9],sf,sg,tr[3][500010],res;
const long long mod=1e9+7;
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline long long ksm(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y&1)
		res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
inline void x_jc()
{
	for(int i=1;i<=7;++i)
	{
		for(int j=1;j<=7;++j)
		{
			fz[i][j]=x[i][j];
			x[i][j]=0;
		}
	}
	for(int i=1;i<=7;++i)
	{
		for(int j=1;j<=7;++j)
		{
			for(int k=1;k<=7;++k)
			{
				x[i][j]=(x[i][j]+fz[i][k]*fz[k][j]%mod)%mod;
			}
		}
	}
}
inline void ans_jc()
{
	for(int i=1;i<=7;++i)
	{
		for(int j=1;j<=7;++j)
		{
			fz[i][j]=ans[i][j];
			ans[i][j]=0;
		}
	}
	for(int i=1;i<=7;++i)
	{
		for(int j=1;j<=7;++j)
		{
			for(int k=1;k<=7;++k)
			{
				ans[i][j]=(ans[i][j]+fz[i][k]*x[k][j]%mod)%mod;
			}
		}
	}
}
inline void ju_ksm(long long x)
{
	while(x)
	{
		if(x&1)
		ans_jc();
		x_jc();
		x>>=1;
	}
}
inline void add(int opt,int x,long long y)
{
	for(int i=x;i<=n;i+=i&(-i))
	tr[opt][i]=(tr[opt][i]+y)%mod;
}
inline long long query(int opt,int x)
{
	long long ji=0;
	for(int i=x;i>=1;i-=i&(-i))
	ji=(ji+tr[opt][i])%mod;
	return ji;
}
int main()
{
	n=read();
	kk=read();
	for(int i=1;i<=n;++i)
	v[i]=read();
	for(int i=1;i<=7;++i)
	ans[i][i]=1;
	ni2=ksm(2,mod-2);
	ni=ksm(n-2,mod-2);//求的时候是n-2种的系数,我们要的是对于一个C的答案,所以除以n-2 
	for(int i=1;i<=7;++i)
	x[i][i]=ni2*(n-2)%mod*(n-3)%mod;
	x[1][2]=n-2;
	x[1][3]=1;
	x[1][6]=n-2;
	x[2][1]=1;
	x[2][2]=(x[2][2]+n-3)%mod;
	x[2][4]=1;
	x[2][5]=1;
	x[2][7]=n-3;
	x[3][1]=1;
	x[3][4]=n-2;
	x[3][5]=n-2;
	x[4][2]=1;
	x[4][3]=1;
	x[4][4]=(x[4][4]+n-3)%mod;
	x[4][6]=1;
	x[4][7]=n-3;
	x[5][2]=1;
	x[5][3]=1;
	x[5][5]=(x[5][5]+n-3)%mod;
	x[5][6]=1;
	x[5][7]=n-3;
	x[6][1]=1;
	x[6][4]=1;
	x[6][5]=1;
	x[6][6]=(x[6][6]+n-3)%mod;
	x[6][7]=n-3;
	x[7][2]=1;
	x[7][4]=1;
	x[7][5]=1;
	x[7][6]=1;
	x[7][7]=(x[7][7]+2*(n-4)+1)%mod;
	ju_ksm(kk);
	for(int i=1;i<=n;++i)
	{
		long long a=query(0,v[i]),b=i-a-1;
		long long fa=query(1,v[i]),ga=query(2,v[i]);
		long long fb=sf-fa,gb=sg-ga;
		res=(res+b*ans[1][1]%mod)%mod;
		res=(res+(a*(n-i)%mod+b*(i-2)%mod)%mod*ans[1][2]%mod*ni%mod)%mod;
		res=(res+a*ans[1][3]%mod)%mod;
		res=(res+(fb+ga)%mod*ans[1][4]%mod*ni%mod)%mod;
		res=(res+(a*(i-2)%mod+b*(n-i)%mod)%mod*ans[1][5]%mod*ni%mod)%mod;
		res=(res+(gb+fa)%mod*ans[1][6]%mod*ni%mod)%mod;
		sf=sf+i-1;
		sg=sg+n-i-1;
		add(0,v[i],1);
		add(1,v[i],i-1);
		add(2,v[i],n-i-1);
	}
	res=(res+(long long)n*(n-1)%mod*ni2%mod*ni2%mod*ans[1][7]%mod)%mod;
	printf("%lld\n",res);
	return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值