• 某考试 T1 arg


    题目描述

    给出一个长度为 m 的序列 A, 请你求出有多少种 1...n 的排列, 满足 A 是它的一个 LIS.

    输入格式

    第一行两个整数 n, m. 接下来一行 m 个整数, 表示 A.

    输出格式

    一行一个整数表示答案.

    样例

    Input:

    5 3
    1 3 4
    

    Output:

    11
    

    数据范围与提示

    对于前 30% 的数据, n ≤ 9;

    对于前 60% 的数据, n ≤ 12;

    对于 100% 的数据, 1 ≤ m ≤ n ≤ 15.

        我们可以很自然的想到用f[S][s]表示目前选了S中的数,并且LIS的状态是s的方案数。这样的话转移就考虑把还没出现的数加入,再考虑其对s的影响就可以找到下一个状态了,但是这里还有两个需要注意的事情:

        1.如何保证最后的LIS中有a[] ?

    这个还是比较好解决的,我们只需要再两个地方判定就行了:1.如果加入的数是给出的a[]中的一个元素a[i],我们需要判断S中是否包含所有a[1],,,a[i-1];2.最后我们计算答案的时候,只能加s状态的LIS=m的f[2^n-1][s] ,因为这样才能保证a[]是LIS中的一个。

        2.看起来状态是有 (2^n)^2 个的,会不会炸掉?

    简单分析就可以知道,s肯定是S的一个子集,因为只有出现过了才可能在LIS的队列中出现。所以其实状态最多只有 3^n个。(但我真的写不出3进制的操作啊,只好用map强行套个log)

        (然后我来回答一下为什么我考试的时候这个题爆零了23333,考试的时候我的程序就和现在的很接近了,但是我把题目看成LIS必须只能是给出的数列而不是有很多LIS共存,然后就怒WA10个点  (出题人来解释一下为什么这样能过样例233333))

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<tr1/unordered_map>
    #include<algorithm>
    #define ll long long
    using namespace std;
    using namespace std::tr1;
    const int maxn=40005;
    unordered_map<int,int> mmp[maxn];
    int ci[66],n,m,sum[maxn],a[20];
    int to[maxn][20],pos[20],tmp[20];
    
    inline void init(){
    	ci[0]=1;
    	for(int i=1;i<=30;i++) ci[i]=ci[i-1]<<1;
    	for(int i=1;i<ci[n];i++) sum[i]=sum[i^(i&-i)]+1;
    	for(int i=1;i<ci[n];i++)
    	    for(int j=1;j<=n;j++){
    	    	to[i][j]=i;
    	    	for(int k=j-1;k<n;k++) if(ci[k]&i){
    	    		to[i][j]^=ci[k];
    	    		break;
    			}
    			to[i][j]|=ci[j-1];
    		}
    }
    
    inline void solve(){
    	for(int i=1;i<=n;i++) if(pos[i]<=1) mmp[ci[i-1]][ci[i-1]]=1;
    	for(int S=1;S<ci[n];S++)
    	    for(int s=S,TS,ts,now;s;s=(s-1)&S) if(mmp[S].count(s)){
    	    	now=mmp[S][s];
    	    	for(int k=1;k<=n;k++) if(!(ci[k-1]&S)){
    	    		if(pos[k]&&(S&tmp[pos[k]])!=tmp[pos[k]]) continue;
    	    		TS=S|ci[k-1];
    	    		ts=to[s][k];
    	    		mmp[TS][ts]+=now;
    			}
    		}
    	
    	int ans=0;
    	for(int i=1;i<ci[n];i++) if(sum[i]==m) ans+=mmp[ci[n]-1][i];
    	printf("%d
    ",ans);
    }
    
    int main(){
    //	freopen("arg.in","r",stdin);
    //	freopen("arg.out","w",stdout);
    	
    	scanf("%d%d",&n,&m),init();
    	for(int i=1;i<=m;i++) scanf("%d",a+i),pos[a[i]]=i;
    	tmp[1]=0;
    	for(int i=2;i<=m;i++) tmp[i]=tmp[i-1]|ci[a[i-1]-1];
    	solve();
    	return 0;
    }
    

      

  • 相关阅读:
    C#windows向窗体传递泛型类
    phoenix与spark整合
    phoenix创建二级索引
    git
    socket详解
    切片
    通过串口读写数据
    Python 跳出多重循环
    Python从json中提取数据
    Python 字典
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8646022.html
Copyright © 2020-2023  润新知