题目大意
「NOIP2021模拟赛8.23 C」棒棒糖女孩(lollipop)
有一个\(1∼n\)的排列,其中有\(m\)个位置上的数缺失了。
已知这个排列的逆序对数恰好为\(k\),求有多少种可能的排列。
问题解决
我们发现\(m\)很小,也就是说可以暴力枚举\(m\)个数的顺序,但是发现时间复杂度是\(O(m!)\)还是太大,考虑折半搜索,先枚举组合数讲\(m\)个数分成两堆,一半在前面,一半在后面,然后其中他们之间的代价就可以算好了,之后再枚举全排列,将他们之间的代价算好,最后用一个桶记录一下逆序对数,将两边的方案结合一下就好了,注意统计答案的时候要预处理出一些东西,方便与\(O(1)\)来更新答案
代码实现
#pragma GCC optimize(2)
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=200005;
int N,a[maxn],pre_max[maxn][15],lst_min[maxn][15],M,b[15],c[maxn],A[15],B[15],len_B,len_A,A_[15],B_[15],G[1<<8][8],bob[maxn<<3],p,stack[maxn],top,sum;
int ans,K;
LL Ans;
int vis[maxn],vis_1[15];
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void add_x(int x,int data){for(int i=x;i<=N;i+=i&-i)c[i]+=data;}
int get(int x){int ret=0;for(int i=x;i;i-=i&-i)ret+=c[i];return ret;}
void DFS_1(int x,int s,int k){
if(x==len_A+1){
bob[k]++;stack[++top]=k;
}
if(A[1]==2)
vis_1[1]=0;
for(int i=1;i<=len_A;i++)if(!(s>>(i-1)&1)){
A_[x]=A[i];DFS_1(x+1,s|(1<<(i-1)),k+G[s|(1<<(i-1))][i]+pre_max[A[i]][x]+lst_min[A[i]][x]);A_[x]=0;
}
}
void DFS_2(int x,int s,int k){
if(x==len_B+1){
ans+=(bob[K-p-k-sum]);
}
for(int i=1;i<=len_B;i++)if(!(s>>(i-1)&1)){
B_[x]=B[i];DFS_2(x+1,s|(1<<(i-1)),k+G[(s|(1<<i-1))][i]+pre_max[B[i]][x+len_A]+lst_min[B[i]][x+len_A]);B_[x]=0;
}
}
void check(){
for(int i=1;i<=top;i++)bob[stack[i]]=0;top=0;
sort(A+1,A+1+len_A);
DFS_1(1,0,0);
sort(B+1,B+1+len_B);
p=0;for(int i=1;i<=len_A;i++)for(int j=1;j<=len_B;j++)(A[i]>B[j])&&(p++,0);
ans=0;
DFS_2(1,0,0);
Ans+=ans;
}
void DFS(int pos){
if(pos==(M)+1){
check();return ;
}
if(len_A<(M>>1)){A[++len_A]=b[pos];DFS(pos+1);A[len_A--]=0;}
if(len_B<(M-(M>>1))){B[++len_B]=b[pos];DFS(pos+1);B[len_B--]=0;}
}
int main(){
freopen("lollipop.in","r",stdin);
freopen("lollipop.out","w",stdout);
N=read();K=read();
for(int i=1;i<=N;i++)a[i]=read(),vis[a[i]]=1;
for(int i=N;i;i--)if(a[i]){sum+=get(a[i]);add_x(a[i],1);}
for(int i=1;i<=N;i++)(!vis[i])&&(b[++M]=i,0);
for(int i=1;i<=M;i++){int num=0,p=0;for(int j=1;j<=N;j++){(a[j]>b[i])&&(num++,0);(!a[j])&&(pre_max[b[i]][++p]=num,0);}}
for(int i=1;i<=M;i++){int num=0,p=M;for(int j=N;j;j--){(a[j]<b[i]&&a[j]>0)&&(num++,0);(!a[j])&&(lst_min[b[i]][p--]=num,0);}}
for(int i=0;i<(1<<(M+1>>1));i++)
for(int j=1;j<=(M+1>>1);j++)if((i>>j-1)&1){
for(int k=j+1;k<=(M+1>>1);k++){
G[i][j]+=(i>>k-1)&1;
}
}
DFS(1);
printf("%lld\n",Ans);
return 0;
}