「NOIP2021模拟赛8.23 C」棒棒糖女孩(lollipop)题解

题目大意

「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;
}
上一篇:SpringBoot 如何统计、监控 SQL运行情况


下一篇:BestCoder17 1002.Select(hdu 5101) 解题报告