zoukankan      html  css  js  c++  java
  • 「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
    ",Ans);
    	return 0;
    }
    
  • 相关阅读:
    同步和异步有何异同?在什么情况下分别使用他们?举例说明
    Java检查异常和非检查异常,运行时异常和非运行时异常的区别
    运行时异常与受检异常有何异同、error和exception有什么区别
    sleep() 和 wait() 有什么区别?
    final, finally, finalize的区别
    HashMap和Hashtable的区别
    & 和 && 的区别
    List集合复制
    Visual Studio 2015 Tools for Unity安装
    发布Android程序
  • 原文地址:https://www.cnblogs.com/martian148/p/15178930.html
Copyright © 2011-2022 走看看