题目大意
因为找不到题面所以就勉为骑♂男C了一张下来
思路
因为题目要求的是最后的总和,所以可以分开每一对a[i]和a[j]来考虑,计算每一对的贡献
对于每一对数,考虑其位置和出现次数,可以分成几类出现次数相同的来讨论
那么每一类的贡献就是
(距离1+距离2+……)*出现次数*(a[i]-a[j])=距离之和*每一对的出现次数*(a[i]-a[j])
a[i]-a[j]可以放到最后再乘
分类讨论
设b[p[i]]表示p[i]在原序列的位置i,枚举a[i]和a[j]的具体数值,然后考虑ij在原序列上的位置(i<j 且 a[i]>a[j]且a[i]!=p[i]且a[j]!=p[j])
(下文为了方便直接用i表示a[i],j表示a[j],也就是i>j且i的位置在j前,同时上面的条件变成了i的位置!=b[i] 且 j的位置!=b[j])
先设f[n]表示当有n个数时的错排数(就是著名的错排公式 )
f
[
n
]
=
(
n
−
1
)
(
f
[
n
−
1
]
+
f
[
n
−
2
]
)
f[n]=(n-1)(f[n-1]+f[n-2])
f[n]=(n−1)(f[n−1]+f[n−2])
其中f[0]=1,f[1]=0
解释一下
对于每个新加入的数n,首先不能将其放在自己的位置上
所以就要和前面(n-1)个数中的一个交换
对于交换出来的数,如果直接将其放在n,那其它的数就不收影响,就是f[n-2]
如果不放在n,也就相当于这个数原来在n时的错排(因为错排就是每个数不能放在自己的位置,其它数不放在自己的位置上,交换的数也不放在n上,就相当于该数原本就在n上,但由于错排不能放在上面),也就是f[n-1]
所以
f
[
n
]
=
(
n
−
1
)
(
f
[
n
−
1
]
+
f
[
n
−
2
]
)
f[n]=(n-1)(f[n-1]+f[n-2])
f[n]=(n−1)(f[n−1]+f[n−2])
f
[
0
]
f[0]
f[0]由于没有所以为1
f
[
1
]
f[1]
f[1]由于不存在所以为0
i和j一定有至少一个在b[i]或b[j]上
b[i]<b[j]
那么就只可能j在b[i]上,i在b[j]上
j在b[i]上
那么j固定了,i可以在1~b[i]-1的任意一个位置(因为i的位置<j的位置,所以i不可能到b[i])
然后出现次数类似于错排公式,i可以在1~b[i]-1的任意一个位置上,将该位置上的数可以放到b[j]或不放b[j]
放的话就直接是f[n-3],不放的话就是f[n-2](等于原来在b[j]上但现在不能放在原位,就是n-2的错排)
所以该种情况的贡献为
(
b
[
i
]
∗
(
b
[
i
]
−
1
)
/
2
)
∗
(
f
[
n
−
3
]
+
f
[
n
−
2
]
)
(b[i]*(b[i]-1)/2)*(f[n-3]+f[n-2])
(b[i]∗(b[i]−1)/2)∗(f[n−3]+f[n−2])
(不用乘以b[i]-1是因为错排公式中每种情况的权值为1,所以要乘上去,但现在每种情况的权值为其长度,已在之前计算过。实际就是求每一对(ij都已固定)的出现次数)
i在b[j]上
那么j可以在b[j]+1~n的任意位置,大致同上
所以该种情况的贡献为
(
1
+
n
−
b
[
j
]
)
∗
(
n
−
b
[
j
]
)
/
2
)
∗
(
f
[
n
−
3
]
+
f
[
n
−
2
]
)
(1+n-b[j])*(n-b[j])/2)*(f[n-3]+f[n-2])
(1+n−b[j])∗(n−b[j])/2)∗(f[n−3]+f[n−2])
b[j]<b[i]
i在b[j]上,j在b[i]上
显然,其它n-2个数与ij没有关系
所以贡献就是
(
b
[
i
]
−
b
[
j
]
)
∗
f
[
n
−
2
]
(b[i]-b[j])*f[n-2]
(b[i]−b[j])∗f[n−2]
j在b[i]上,且i不在b[j]上
因为i不在b[j]上,所以长度总和=1~b[i]-1的和-(b[i]-b[j])(就是除掉i在b[j]上的情况)
然后次数和之前类似,也是f[n-3]+f[n-2]
所以贡献为
(
b
[
i
]
∗
(
b
[
i
]
−
1
)
/
2
−
(
b
[
i
]
−
b
[
j
]
)
)
∗
(
f
[
n
−
3
]
+
f
[
n
−
2
]
)
(b[i]*(b[i]-1)/2-(b[i]-b[j]))*(f[n-3]+f[n-2])
(b[i]∗(b[i]−1)/2−(b[i]−b[j]))∗(f[n−3]+f[n−2])
i在b[j]上,且j不在b[i]上
同上。
(
(
1
+
n
−
b
[
j
]
)
∗
(
n
−
b
[
j
]
)
/
2
−
(
b
[
i
]
−
b
[
j
]
)
)
∗
(
f
[
n
−
3
]
+
f
[
n
−
2
]
)
((1+n-b[j])*(n-b[j])/2-(b[i]-b[j]))*(f[n-3]+f[n-2])
((1+n−b[j])∗(n−b[j])/2−(b[i]−b[j]))∗(f[n−3]+f[n−2])
i和j都不在b[i]和b[j]上
求总和
考虑用容斥求总和。
首先可以O(n)算出总的长度和,再减去所有不合法的情况
不合法的情况是ij中有至少一个在b[i]或b[j]上的4种情况
然后发现b[i]~b[j]这一段被减了两次,要再加回去
设l为min(b[i],b[j]),r为max(b[i],b[j]),则总和就是
s
u
m
−
(
r
∗
(
r
−
1
)
/
2
)
−
(
(
1
+
n
−
l
)
∗
(
n
−
l
)
/
2
)
−
(
l
∗
(
l
−
1
)
/
2
)
−
(
(
1
+
n
−
r
)
∗
(
n
−
r
)
/
2
)
+
(
r
−
l
)
sum-(r*(r-1)/2)-((1+n-l)*(n-l)/2)-(l*(l-1)/2)-((1+n-r)*(n-r)/2)+(r-l)
sum−(r∗(r−1)/2)−((1+n−l)∗(n−l)/2)−(l∗(l−1)/2)−((1+n−r)∗(n−r)/2)+(r−l)
然后实际和lr的顺序每多大关系,可以再优化(见最终代码)
那么对于每一对ij,考虑换出来的两个数的位置
有两个数在b[i]b[j]上
那么显然,就是f[n-4]
但因为换出来的数可以交换,所以实际上是
f
[
n
−
4
]
∗
2
f[n-4]*2
f[n−4]∗2
有一个数在b[i]b[j]上
也很显然,就是f[n-3](另一个数不在另一个位置上)
但实际上有4(两个数*两个位置)种情况,贡献为
f
[
n
−
3
]
∗
4
f[n-3]*4
f[n−3]∗4
一个数都不在b[i]b[j]上
由于这个问题过于哲♂学所以先不讨论
another problem
考虑另一个问题:
求在长度为n的错排下,且有两个数不能放入另外两个位置中的任意一个
还是考虑容斥,设这两个数为i和j且ij都在其原来的位置上,则不合法的情况有
(X表示另一个数不在该位置)
由于是错排,首先加上f[n]
那么所有i和j在原位的情况都被考虑了
然后减去f[n-1]*2,也就是ij有一个在上面的情况
最后减去f[n-2],也就是两个都在上面的情况
所以最终的答案为
f
[
n
]
f[n]
f[n]
−
-
−
f
[
n
−
1
]
∗
2
f[n-1]*2
f[n−1]∗2
−
-
−
f
[
n
−
2
]
f[n-2]
f[n−2]
原来的问题
因为原来的i和j都已被固定,所以就相当于上面长度为(n-2)时的问题
所以贡献为
f
[
n
−
2
]
−
f
[
n
−
3
]
∗
2
−
f
[
n
−
4
]
f[n-2]-f[n-3]*2-f[n-4]
f[n−2]−f[n−3]∗2−f[n−4]
真是奇♂妙
所以这一种情况的贡献为
(
s
u
m
−
(
r
∗
(
r
−
1
)
/
2
)
−
(
(
1
+
n
−
l
)
∗
(
n
−
l
)
/
2
)
−
(
l
∗
(
l
−
1
)
/
2
)
−
(
(
1
+
n
−
r
)
∗
(
n
−
r
)
/
2
)
+
(
r
−
l
)
)
∗
(
f
[
n
−
4
]
∗
2
+
f
[
n
−
3
]
∗
4
+
(
f
[
n
−
2
]
−
f
[
n
−
3
]
∗
2
−
f
[
n
−
4
]
)
)
(sum-(r*(r-1)/2)-((1+n-l)*(n-l)/2)-(l*(l-1)/2)-((1+n-r)*(n-r)/2)+(r-l))*(f[n-4]*2+f[n-3]*4+(f[n-2]-f[n-3]*2-f[n-4]))
(sum−(r∗(r−1)/2)−((1+n−l)∗(n−l)/2)−(l∗(l−1)/2)−((1+n−r)∗(n−r)/2)+(r−l))∗(f[n−4]∗2+f[n−3]∗4+(f[n−2]−f[n−3]∗2−f[n−4]))
最后要乘上(i-j)
code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define mod 1000000009
using namespace std;
long long f[5001];
long long b[5001];
int n;
long long i,j,l,r,ans,ans2,sum;
int main()
{
freopen("derangement.in","r",stdin);
freopen("derangement.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d",&j);
b[j]=i;
}
if (n==2)
{
printf((b[1]<b[2])?"0\n":"1\n");
return 0;
}
fo(i,1,n-1)
sum=(sum+i*(n-i))%mod;
f[0]=1;
fo(i,2,n)
f[i]=(i-1)*(f[i-1]+f[i-2])%mod;
fo(i,2,n)
{
fo(j,1,i-1)
{
ans2=0;
if (b[i]<b[j])
{
ans2=(ans2+(b[i]*(b[i]-1)/2%mod)*(f[n-3]+f[n-2])%mod)%mod;
ans2=(ans2+((1+n-b[j])*(n-b[j])/2%mod)*(f[n-3]+f[n-2])%mod)%mod;
}
else
{
ans2=(ans2+(b[i]-b[j])*f[n-2])%mod;
ans2=(ans2+(b[i]*(b[i]-1)/2%mod-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
ans2=(ans2+((1+n-b[j])*(n-b[j])/2%mod-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
}
if (n>=4)
{
l=min(b[i],b[j]);
r=max(b[i],b[j]);
ans2=(ans2+(sum-(r*(r-1)/2%mod)-((1+n-l)*(n-l)/2%mod)-(l*(l-1)/2%mod)-((1+n-r)*(n-r)/2%mod)+(r-l))%mod*
(f[n-4]*2+f[n-3]*4+(f[n-2]-f[n-3]*2-f[n-4]))%mod)%mod;
}
ans=(ans+ans2*(i-j))%mod;
}
}
printf("%lld\n",(ans+mod)%mod);
fclose(stdin);
fclose(stdout);
return 0;
}
code·最终版
每次求前后的长度和可以预处理,l和r也可以省略
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define abs(x) ((x>0)?(x):-(x))
#define mod 1000000009
using namespace std;
long long A[5001];
long long B[5001];
long long f[5001];
long long b[5001];
int n;
long long i,j,l,r,ans,ans2,sum,S;
int main()
{
freopen("derangement.in","r",stdin);
freopen("derangement.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d",&j);
b[j]=i;
}
if (n==2)
{
printf((b[1]<b[2])?"0\n":"1\n");
return 0;
}
fo(i,1,n)
{
A[i]=b[i]*(b[i]-1)/2%mod;
B[i]=(1+n-b[i])*(n-b[i])/2%mod;
}
fo(i,1,n-1)
sum=(sum+i*(n-i))%mod;
f[0]=1;
fo(i,2,n)
f[i]=(i-1)*(f[i-1]+f[i-2])%mod;
S=(f[n-4]*2+f[n-3]*4+(f[n-2]-f[n-3]*2-f[n-4]))%mod;
fo(i,2,n)
{
fo(j,1,i-1)
{
ans2=0;
if (b[i]<b[j])
{
ans2=(ans2+A[i]*(f[n-3]+f[n-2])%mod)%mod;
ans2=(ans2+B[j]*(f[n-3]+f[n-2])%mod)%mod;
}
else
{
ans2=(ans2+(b[i]-b[j])*f[n-2])%mod;
ans2=(ans2+(A[i]-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
ans2=(ans2+(B[j]-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
}
if (n>=4)
ans2=(ans2+(sum-A[i]-B[i]-A[j]-B[j]+abs(b[i]-b[j]))*S)%mod;
ans=(ans+ans2*(i-j))%mod;
}
}
printf("%lld\n",(ans+mod)%mod);
fclose(stdin);
fclose(stdout);
return 0;
}