NOIP模拟 上升序列(最长上升子序列+状压DP)

【题目描述】

给出一个长度为 m 的上升序列 A(1 ≤ A[i]≤ n), 请你求出有多少种 1...n 的排列, 满足 A 是它的一个 LIS.

【输入格式】

第一行两个整数 n,m.

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

【输出格式】

一行一个整数表示答案.

【样例输入】

5 3

1 3 4

【输出格式】

11

【备注】

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

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

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

【题目分析】

一看这个数据范围,觉得像状压DP,但打死想不出压什么,打了个深搜。。。滚粗。。。

最长不降子序列的一种求法是:使用附加数组D[i],表示当前长度为 i的最长不降子序列最小的结尾是多少,显然它是单调递增的。每插入一个数就二分找到第一个大于它的位置替换。 

我们可以设状态S1,第i位为 1表示它在D 中出现了,出现的位置就是1~i位1 的数量 

现在就可以设递推状态f(S,S1)表示当前用了S中的数,D的状态为S1的方案数 

转移很容易转移,枚举这一位选什么直接更新  

注意到S1一定是S的子集,于是可以用三进制表示  

复杂度O(3^n∗ n)实际上远远不到 

【代码~】

#include   
#define fo(i,a,b) for(int i=a;i<=b;++i)   
#define fod(i,a,b) for(int i=a;i>=b;--i)   
#define N 16   
#define M 14348910   
#define LL long long   
using namespace std;   
int n,m,cf[N],a[N],wz[N],cf2[N];   
LL f[M],ans;  
 
void dfs(int k,int lim,int v)   
{   
    if(k>n)   
    {   
        if(lim==0) ans+=f[v];   
        return;   
 	}
    v+=cf[k-1];   
    dfs(k+1,lim,v);   
    v+=cf[k-1];   
	dfs(k+1,lim-1,v);   
}  
 
int main()   
{   
    cin>>n>>m;   
    fo(i,1,m) 
	  scanf("%d",&a[i]),wz[a[i]]=i;   
    cf[0]=cf2[0]=1;   
    fo(i,1,n) 
	  cf[i]=cf[i-1]*3,cf2[i]=cf2[i-1]*2;   
    f[0]=1;   
    fo(i,0,cf2[n]-2)   
    {   
        bool pd=1,c1=1;   
        fo(j,1,m)    
        {   
            if((i&cf2[a[j]-1])==0) 
			  pd=0;   
            else 
			  if(!pd)   
              {    
                  c1=0;   
                  break;   
              }   
        }   
        if(c1)   
        {   
            for(int i1=i;1;i1=(i1-1)&i)   
            {   
            	int vi=0;   
            	fo(j,1,n)    
            	{   
                	if(i1&cf2[j-1]) 
				      vi+=cf[j-1];   
                	if(i&cf2[j-1]) 
				      vi+=cf[j-1];   
            	}    
                if(f[vi])   
                  fo(j,1,n)   
                  {   
                      if(vi/cf[j-1]%3==0)   
                      {   
                          int v=vi+2*cf[j-1];   
                          fo(p,j+1,n)    
                            if(v/cf[p-1]%3==2)    
                            {   
                            	v-=cf[p-1];   
                                break;   
                            }   
                    	  f[v]+=f[vi];   
                      }   
                  }   
                  if(i1==0) 
				    break;   
            }   
        }   
    }   
    dfs(1,m,0);   
    printf("%lld\n",ans);
    return 0;   
}   

 

转载于:https://www.cnblogs.com/Ishtar/p/10010834.html

你可能感兴趣的:(NOIP模拟 上升序列(最长上升子序列+状压DP))