题意:
n个物品编号为1~n,每个物品i有一个不能放的位置pi,保证p是一个排列。对于合法排列的每一对逆序<j,i>(j>i),它的贡献是 ( p o s i − p o s j ) ∗ ( j − i ) (pos_i-pos_j)*(j-i) (posi−posj)∗(j−i),问所有合法排列中的所有逆序的贡献的总和。(n<=1000)
题解:
首先我们注意到每一对物品的贡献可以单独求解,所以我们求每一对(i,j)的贡献,把它们加起来。
对于一对i和j,如果
p
o
s
i
和
p
o
s
j
posi和pos_j
posi和posj确定了,
(
p
o
s
i
−
p
o
s
j
)
∗
(
j
−
i
)
(pos_i-pos_j)*(j-i)
(posi−posj)∗(j−i)就确定了,这个对应位置可以贡献的答案是
(
p
o
s
i
−
p
o
s
j
)
∗
(
j
−
i
)
∗
剩
余
数
字
的
合
法
排
列
数
(pos_i-pos_j)*(j-i)*剩余数字的合法排列数
(posi−posj)∗(j−i)∗剩余数字的合法排列数。
剩余数字有三种情况:
- i占了pj, j占了pi,剩余的每个数字都有一个不能放的位置,那么合法排列数就是全错位排列f[n]
- i和j其中一个占了另一个的位置,另一个占了别的数字的位置,剩余数字有一个数字可以随便放,令它的排列数为d[n]
- i和j都没占对方的位置,那么剩余数字有两个可以随便放,令它的排列数为g[n]
d[n]和g[n]可以dp求解。
对于d[n],如果那个自由数字占了那个自由的空,剩余排列就是f[n-1],如果占了其他数字的空,剩余排列是d[n-1],有(n-1)个其他数字,所以
d
[
n
]
=
f
[
n
−
1
]
+
(
n
−
1
)
d
[
n
−
1
]
d[n]=f[n-1]+(n-1)d[n-1]
d[n]=f[n−1]+(n−1)d[n−1]
同理讨论两个自由元的位置可得
g
[
n
]
=
2
f
[
n
−
2
]
+
4
(
n
−
2
)
d
[
n
−
2
]
+
(
n
−
2
)
(
n
−
3
)
g
[
n
−
2
]
g[n]=2f[n-2]+4(n-2)d[n-2]+(n-2)(n-3)g[n-2]
g[n]=2f[n−2]+4(n−2)d[n−2]+(n−2)(n−3)g[n−2]
然后分别求出每一对i和j三种情况下
∣
p
o
s
i
−
p
o
s
j
∣
|pos_i-pos_j|
∣posi−posj∣的贡献,最后乘上(j-i)就是(i,j)的贡献了。
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 1e3 + 50;
const ll mod = 998244353;
ll qm(ll a, ll b){
ll res = 1;
while(b){
if(b&1) res = res*a%mod;
a = a*a%mod;
b >>= 1;
}return res;
}
ll fac[maxn], ifac[maxn], f[maxn], d[maxn], g[maxn];
int p[maxn];
int main()
{
fac[0] = ifac[0] = 1;
for(int i = 1; i < maxn; ++i) {
fac[i] = fac[i-1]*i%mod;
ifac[i] = qm(fac[i], mod-2);
}
f[0] =1 ; f[1] = 0;
for(int i = 2; i < maxn; ++i){
f[i] = 0;
for(int j = 2; j <= i; ++j){
if(j&1){
f[i] = (f[i] - fac[i]*ifac[j]%mod)%mod;
}
else f[i] = (f[i] + fac[i]*ifac[j]%mod)%mod;
}
}
d[0] = d[1] = 1; d[2] = 1; d[3] = 3;
g[0] = g[1] = 1; g[2] = 2; g[3] = 4;
for(int i = 4; i < maxn; ++i){
d[i] = (f[i-1] + (i-1)*d[i-1])%mod;
g[i] = (2*f[i-2]%mod + 4*(i-2)%mod*d[i-2]%mod + (i-2)*(i-3)%mod*g[i-2]%mod)%mod;
}
int T;cin>>T;
while(T--){
ll n; cin>>n;
for(int i = 1; i <= n; ++i){
scanf("%d", &p[i]);
}
if(n <= 1){
printf("0\n"); continue;
}
ll k = n*(n+1)%mod*(2*n+1)%mod*qm(12, mod-2)%mod - n*(n+1)%mod*qm(4, mod-2);
k%=mod;
ll ans = 0;
for(int i = 1; i < n; ++i){
for(int j = i+1; j <= n; ++j){
ll tempk = k;
if(p[j] < p[i]){//两个都命中
tempk -=(p[i] - p[j]);
}
//命中p[i]
if(p[j] < p[i]){
tempk -= p[i]*(p[i]-1)/2 - (p[i] - p[j]);
}
else tempk -= p[i]*(p[i]-1)/2;
//命中p[j]
if(p[j] < p[i]){
tempk -= (n-p[j]+1)*(n-p[j])/2 - (p[i] - p[j]);
}
else tempk -= (n-p[j]+1)*(n-p[j])/2;
tempk %= mod;
tempk = (tempk+mod)%mod;
//cout<<"tempk:"<<tempk<<endl;
ll temp = tempk*g[n-2]%mod;//
//cout<<"temp:"<<temp<<endl;
//i和j互占位置
if(p[j] > p[i]){
temp = (temp - (p[j] - p[i])*g[n-2])%mod;
temp += (p[j] - p[i])*f[n-2]%mod;
temp %= mod;
}
// cout<<"t:"<<temp<<endl;
//i在p[j],j不在p[i]
if(p[j] < p[i]){//如果可占的位置中没有p[i]
temp -= g[n-2] * (p[j]*(p[j]-1)/2)%mod;
temp %= mod;
temp += d[n-2] * (p[j]*(p[j]-1)/2) %mod;
temp %= mod;
}
else {
// cout<<"p:"<<p[j]<<endl;
temp -= ( (p[j]*(p[j]-1)/2 ) - (p[j] - p[i]) )*g[n-2]%mod;
temp %= mod;
temp += ( (p[j]*(p[j]-1)/2) - (p[j] - p[i]) )*d[n-2]%mod;
temp %= mod;
}
//cout<<"temp:"<<temp<<endl;
//j在p[i], i不在p[j]
if(p[i] > p[j]){//i可占的位置中没p[j]
temp -= ( (n-p[i])*(n-p[i]+1)/2 )*g[n-2] %mod;
temp%= mod;
temp += ( (n-p[i])*(n-p[i]+1)/2 )*d[n-2] %mod;
temp %= mod;
}
else{
temp -= ((n-p[i])*(n-p[i]+1)/2 - (p[j] - p[i]))*g[n-2]%mod;
temp %= mod;
temp += ((n-p[i])*(n-p[i]+1)/2 - (p[j] - p[i]))*d[n-2]%mod;
temp %= mod;
}
//cout<<"i:"<<i<<" j:"<<j<<" temp:"<<temp<<endl;
ans = (ans + temp*(j-i))%mod;
}
}
ans %= mod;
ans = (ans + mod)%mod;
cout<<ans<<endl;
}
}