题意:
给你一个长度为
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
<
=
500000
,
k
<
=
1
e
9
n<=500000,k<=1e9
n<=500000,k<=1e9
题解:
这个题真的是我花了一整天的时间才差不多明白,但是也不敢说理解的非常好。感觉网上的题解都写得太简略,似乎有一篇稍微详细一点的还有一些错误,于是导致我很久都没有明白题解在说什么。幸亏有大佬dsgsjk帮我,让我能够比较明白这个题的做法。于是我来照着我的理解说一下这个题的做法。
我们的一个想法是对每一个位置对 ( A , B ) (A,B) (A,B)来考虑统计答案(假设 A < B A<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 & 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 \\ \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡Cn−22110010n−2Cn−22+n−30110110Cn−22110001n−2Cn−22+n−301101n−20Cn−22+n−311n−20011Cn−22+n−310n−30n−3n−3n−3Cn−22+2(n−4)+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]
b∗x[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∗(n−B)+b∗(B−2)]∗x[1][2]∗n−21
看作把
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
B−2个位置,后面有
n
−
B
n-B
n−B个位置。这里统一解释一下这6种情况中所有含有
C
C
C的情况都要除以
n
−
2
n-2
n−2的原因,原因是在矩阵乘法的时候,我们是把所有等价的
C
C
C的总数一起算了,这里实际上是要具体一个
C
C
C的总方案数,由于之前说的每一个
C
C
C是等价的,所以要把矩阵乘法算出来的方案除以
n
−
2
n-2
n−2。
(
B
,
A
)
(B,A)
(B,A):
a
∗
x
[
1
]
[
3
]
a*x[1][3]
a∗x[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]∗n−21
看作把
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∗(B−2)+b∗(n−B)]∗x[1][5]∗n−21
和
(
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]
Cn2∗21∗x[1][7]
这里对所有的
C
C
C一起算,就不去除
n
−
2
n-2
n−2了,对于这些
C
C
C,有
C
n
2
C_{n}^2
Cn2种组合,每两种组合只会产生一个逆序对,也就是对于两个
C
C
C,他们的值分别是
(
x
,
y
)
(x,y)
(x,y),那么要么
x
>
y
x>y
x>y,要么
y
>
x
y>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;
}