题目
点这里,看题目
题解
题意
给出一个
K
K
K、
P
P
P和被重新定义了的字典序
求出在逆序对个数
K
K
K的情况下,“字典序”第
P
P
P小的排列
分析
可以设
F
[
i
]
[
j
]
F[i][j]
F[i][j]表示前
i
i
i个位置逆序对个数
j
j
j的方案数
那么转移显而易见:
F
[
i
]
[
j
]
=
∑
j
−
i
+
1
−
>
j
i
−
1
F[i][j]=∑_{j-i+1->j}^{i-1}
F[i][j]=∑j−i+1−>ji−1
可知,这个转移的时间复杂度是
O
(
N
2
K
)
O(N^2K)
O(N2K)
由于
N
≤
1000
,
K
≤
1
0
4
N≤1000,K≤10^4
N≤1000,K≤104,很明显这个转移会TLE
那么思考优化
我们知道,当
F
[
i
]
[
j
]
>
P
F[i][j]>P
F[i][j]>P时,它对答案是没有任何贡献的
考虑设
B
[
i
]
[
j
]
B[i][j]
B[i][j],记录
F
[
i
]
[
j
]
F[i][j]
F[i][j]和
P
P
P的关系
当
F
[
i
]
[
j
]
>
P
F[i][j]>P
F[i][j]>P时,我们不需要考虑它
亦或者,当我们在计算
F
[
i
]
[
j
]
F[i][j]
F[i][j]时,如果有一个
F
[
i
−
1
]
[
k
]
F[i-1][k]
F[i−1][k]是大于
P
P
P的,那么
F
[
i
]
[
j
]
F[i][j]
F[i][j]势必会大于
P
P
P
又比如说:在计算
F
[
i
]
[
j
]
F[i][j]
F[i][j]的过程中,加完某个数后
F
[
i
]
[
j
]
>
P
F[i][j]>P
F[i][j]>P,那么
B
[
i
]
[
j
]
B[i][j]
B[i][j]也要更新
求出所有对答案有贡献的
F
[
i
]
[
j
]
F[i][j]
F[i][j]后,开始处理队列
按照给出的字典序暴力枚举
设
S
[
i
]
S[i]
S[i]表示
i
i
i能对后面贡献多少个逆序对
初始化
S
[
i
]
=
i
−
1
S[i]=i-1
S[i]=i−1
若当前处理到第
i
i
i个位置
那么有式子
F
[
i
−
1
]
[
K
−
s
[
g
[
i
]
[
j
]
]
]
F[i-1][K-s[g[i][j]]]
F[i−1][K−s[g[i][j]]](注意,这里的
i
i
i是倒着来的,读入时也要倒着读入)
此时,
F
[
i
−
1
]
[
K
−
F
[
i
−
1
]
[
K
−
s
[
g
[
i
]
[
j
]
]
]
]
F[i-1][K-F[i-1][K-s[g[i][j]]]]
F[i−1][K−F[i−1][K−s[g[i][j]]]]和
P
P
P就有两种情况
1:
F
[
i
−
1
]
[
K
−
s
[
g
[
i
]
[
j
]
]
]
≥
P
F[i-1][K-s[g[i][j]]]≥P
F[i−1][K−s[g[i][j]]]≥P
2:
F
[
i
−
1
]
[
K
−
s
[
g
[
i
]
[
j
]
]
]
<
P
F[i-1][K-s[g[i][j]]]<P
F[i−1][K−s[g[i][j]]]<P
情况1:这说明当前这个位置
i
i
i是正确的,那么输出,更新
K
=
K
−
s
[
g
[
i
]
[
j
]
]
K=K-s[g[i][j]]
K=K−s[g[i][j]],更新
S
[
g
[
i
]
[
j
]
+
1
−
>
n
]
S[g[i][j]+1->n]
S[g[i][j]+1−>n],进入下个数的选择
情况2:这说明
i
i
i还是太小,继续枚举下一个数,更新
P
=
P
−
F
[
i
−
1
]
[
K
−
s
[
g
[
i
]
[
j
]
]
]
P=P-F[i-1][K-s[g[i][j]]]
P=P−F[i−1][K−s[g[i][j]]]
附加说明:若当前这个数选完
N
N
N个后都未出现情况1,那么输出“-1”,即总方案数小于
P
P
P
结合代码理解
Code
#include<cstdio>
using namespace std;
int n,m,p,i,j,k,num,a[1005][1005],f[1005][10005],s[1005];
bool bz,b[1005][10005],bj[1005];
int main()
{
freopen("ocd.in","r",stdin);
freopen("ocd.out","w",stdout);
scanf("%d%d%d",&n,&m,&p);
for (i=n;i>=1;i--)
for (j=1;j<=n;j++)
scanf("%d",&a[i][j]);
f[0][0]=1;
for (i=1;i<n;i++)
{
for (j=0;j<=m;j++)
{
if (b[i][j]==true) continue;
for (k=j;k>=j-i+1;k--)
{
if (b[i-1][k]==true)
{
b[i][j]=true;
break;
}
f[i][j]+=f[i-1][k];
if (f[i][j]>=p)
{
b[i][j]=true;
break;
}
}
}
}
for (i=1;i<=n;i++)
s[i]=i-1;
bz=false;
for (i=n;i>=1;i--)
{
for (j=1;j<=n;j++)
{
if (bj[a[i][j]]==false)
{
if (f[i-1][m-s[a[i][j]]]>=p||b[i-1][m-s[a[i][j]]])
{
bz=true;
bj[a[i][j]]=true;
m-=s[a[i][j]];
for (k=a[i][j]+1;k<=n;k++)
s[k]--;
printf("%d ",a[i][j]);
break;
}
p-=f[i-1][m-s[a[i][j]]];
}
}
if (bz==false)
{
printf("-1\n");
break;
}
}
fclose(stdin);
fclose(stdout);
return 0;
}