这道题如果用我的方法分解比较多,所以我注释了一下代码,方便大家看_(:з」∠)_.
A.问题转化
求所有可能的不同序列的逆序数期望.
很容易知道所有不同情况有
v
a
c
!
vac!
vac!种,
v
a
c
vac
vac是空位置的个数,这样我们就直接求所有不同序列的逆序数之和就行了.
B.分三步求解
把已经填入的数叫做已知数,把没有填入的数叫未知数.
考虑三方面的因素:
1.已知数和已知数之间的贡献 a n s ans ans:
显然如果把未知数都从数列中拿走,这道题就变成了求数列逆序数.经典的做法是树状数组
O
(
n
l
g
n
)
O(nlgn)
O(nlgn).并且已知数和已知数对每个不同的数列贡献为
1
1
1.
a
n
s
=
v
a
c
!
∑
i
=
1
n
∑
j
=
i
+
1
n
[
a
[
i
]
≠
−
1
]
[
a
[
j
]
≠
−
1
]
[
a
[
i
]
>
a
[
j
]
]
ans=vac!\sum_{i=1}^n\sum_{j=i+1}^n[a[i]\neq -1][a[j]\neq -1][a[i]>a[j]]
ans=vac!i=1∑nj=i+1∑n[a[i]̸=−1][a[j]̸=−1][a[i]>a[j]]
2.已知数和未知数之间的贡献 r e s res res:
对于每一个未知的空,他出现在了 ( k − 1 ) ! (k-1)! (k−1)!个序列中,所以每个未知的空的贡献是 ( k − 1 ) ! (k-1)! (k−1)!.
对于每一个未知的空,他填入i的贡献是后面小于i的已知数的个数和前面大于i的已知数的个数。但这样不好统计.
考虑每个已知数的贡献。现在我们知道已知数前面有
a
a
a个空,后面有
b
b
b个空.考虑这样一个转移:
s
u
f
[
i
]
=
[
h
s
[
i
]
]
b
[
i
]
+
s
u
f
[
i
+
1
]
,
p
r
e
[
i
]
=
[
h
s
[
i
]
]
a
[
i
]
+
p
r
e
[
i
+
1
]
.
suf[i]=[hs[i]]b[i]+suf[i+1],\\ pre[i]=[hs[i]]a[i]+pre[i+1].
suf[i]=[hs[i]]b[i]+suf[i+1],pre[i]=[hs[i]]a[i]+pre[i+1].
那么显然答案就是这两个数列求和.
r
e
s
=
∑
i
=
1
n
(
1
−
[
h
s
[
i
]
]
)
(
p
r
e
[
i
]
+
s
u
f
[
i
]
)
.
res=\sum_{i=1}^{n} (1-[hs[i]])(pre[i]+suf[i]).
res=i=1∑n(1−[hs[i]])(pre[i]+suf[i]).
3.未知数和未知数之间的贡献 r e c rec rec:
显然等于所有
v
a
c
vac
vac长度的不同排列的逆序数之和.
这里给出一个递推式,可以
O
(
n
)
O(n)
O(n)算出所有
1...
v
a
c
1...vac
1...vac长度的答案:
r
e
c
[
i
]
=
r
e
c
[
i
−
1
]
⋅
i
+
i
(
i
−
1
)
2
⋅
f
a
c
[
i
−
1
]
.
rec[i]=rec[i-1]\cdot i+\frac{i(i-1)}{2}\cdot fac[i-1].
rec[i]=rec[i−1]⋅i+2i(i−1)⋅fac[i−1].
题目答案等于:
o u t = a n s ⋅ v a c ! + r e s ⋅ ( v a c − 1 ) ! + r e c [ v a c ] v a c ! . out=\frac{ans\cdot vac!+res\cdot (vac-1)!+rec[vac]}{vac!}. out=vac!ans⋅vac!+res⋅(vac−1)!+rec[vac].
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#define lowbit(_x) ((_x)&-(_x))
using namespace std;
const long long mod=998244353;
//a:数组
int a[200005],n;
//hs:hs[i]对应数组中是否存在i
bool hs[200005];
//ask:每个已知数对未知数的贡献
//fac:阶乘
//rec:未知数和未知数之间的贡献
//bit:树状数组
long long ask[200005],fac[200005],rec[200005],bit[200005];
//快速幂
long long qp(long long a,long long b){
long long r=1;
while(b){
if(b&1)r=r*a%mod;
a=a*a%mod;
b>>=1;
}
return r;
}
//树状数组
void add(int x){
while(x<=n){
bit[x]++;
x+=lowbit(x);
}
return ;
}
long long sum(int x){
long long r=0;
while(x>0){
r+=bit[x];
x-=lowbit(x);
}
return r;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
//res:未知数和已知数之间的贡献
//ans:已知数和已知数之间的贡献
//pre:前缀变量
//suf:后缀变量
//vac:未知数个数统计
long long res=0,ans=0,pre=0,suf=0,vac=0;
rec[1]=0;
fac[0]=fac[1]=1;
for (int i=2;i<=2e5+3;i++){
fac[i]=fac[i-1]*i%mod;
rec[i]=rec[i-1]*i%mod+(((((long long)i-1)*i/2)%mod)*fac[i-1])%mod;
rec[i]%=mod;
}
suf=0;
for(int i=n;i>=1;i--){
if(a[i]==-1)suf++,vac++;
else ask[a[i]]=suf,hs[a[i]]=1;
}
suf=0;
for(int i=n;i>=1;i--){
if(!hs[i])res+=suf;
if(res>=mod)res-=mod;
suf+=ask[i];
if(suf>=mod)suf-=mod;
}
pre=0;
for(int i=1;i<=n;i++){
if(a[i]==-1)pre++;
else ask[a[i]]=pre;
}
pre=0;
for(int i=1;i<=n;i++){
if(!hs[i])res+=pre;
if(res>=mod)res-=mod;
pre+=ask[i];
if(pre>=mod)pre-=mod;
}
for(int i=n;i>=1;i--){
if(a[i]!=-1){
ans=ans+sum(a[i]);
add(a[i]);
}
}
res=((res*fac[vac-1]%mod+rec[vac])%mod)*qp(fac[vac],mod-2)%mod+ans;
printf("%I64d\n",res%mod);
}