Description
Input
Output
Sample Input
5 6 7
3 1 4 2 5
2 4 5 3 1
2 4 1 3 5
1 4 3 5 2
5 2 1 4 3
Sample Output
4 2 3 5 1
Data Constraint
正解
首先考虑求出逆序对方案数,设f[i][j]表示当前第i位有了j个逆序对时的方案数。由于每个i最多贡献i-1个逆序对,那么f[i][j]+=f[i-1][l](l=max(0,j-i+1)至j)
但是这么做就爆了,但是我们只要知道它的逆序对有没有大于p就行了,那么这个时候就可以用个数组存一下是否大于p,若大于我们也就不用枚举了。
之后考虑方案,1到n每位枚举,然后每个位置按照它给你的字典序来枚举,然后按照现在的方案数判断,然后搞搞就行了。
#include<cstdio>
#include<iostream>
#define N 1007
using namespace std;
int n,k,p,g[N][N],s[N],ans[N];
long long f[N][10*N];
bool large[N][10*N],bz[N];
int main(){
freopen("ocd.in","r",stdin);
freopen("ocd.out","w",stdout);
scanf("%d%d%d",&n,&k,&p);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&g[i][j]);
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++){
for(int l=j;l>=max(0,j-i+1);l--){
if(large[i-1][l]){
large[i][j]=1;
break;
}
f[i][j]+=f[i-1][l];
if(f[i][j]>=p){
large[i][j]=1;
break;
}
}
}//逆序对方案数
int t=0,cnt=0;//t是现在有几个逆序对,cnt指跳过了几个方案数
bool op=true;
for(int i=1;i<=n;i++) s[i]=i-1;//这个是指i的逆序对贡献,一开始为i-1
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int q=g[i][j];
if(bz[q]) continue;//原来选过的跳过
int num=s[q];
int a=k-t-num;
if(t+num>k) continue;//如果当前的逆序对>k就continue
else{
if(cnt+f[n-i][a]>=p||large[n-i][a]){//当原来方案数+现在方案数>=p时,则说明这个位置必须放目前枚举的数
t+=num;//逆序对加上
ans[i]=q;
bz[q]=1;
for(int l=q+1;l<=n;l++) s[l]--;//q在前面,q+1至n的贡献减一,本来应该用树状数组的,但是数据水
break;
}cnt+=f[n-i][a];//无论是否选,方案数要加
}
}
if(!ans[i]){
op=false;
break;
}
}
if(op) for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
else printf("-1");
}