Jzoj5600 Arg

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

dp套dp好题!

我们先要考虑怎么搞一个状态出来

考虑做lis的两种方法:

1.设f[i]表示做到i的最长子序列长度

2.设f[i]表示长度为i的上升子序列最后那个数最小是多少

第一种方式不好设状态,我们考虑第二种

显然,如果知道f里有哪些数字,就可以还原f

那么我们设一个状态f[S][F]表示现在原来的序列中已经用了S这些数字,f数组中的数为F的方案数

考虑转移,显然有2种方式:
1.向S中添加一个不在A中的元素,这种情况直接转就可以了

2.向S中添加一个在A中的元素,这个时候要考虑顺序,如果A[1..j]都在F中出现过,那么我们可以添加的元素就是A[j+1]

最后答案就是∑ f[S][F] 其中S为全集,F的集合大小为A的长度

这样不能满分,考虑简化状态,由于F∈ S,所以这个状态可以用3进制表示,这样就可以通过了

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int d3[20]={1},ans=0;
int n,m,v[20],g[20],p[20],s[20],f[20000000];
inline void cal(int i,int& j,int& k){
	for(j=k=0;i;i/=3){
		if(i%3==1) ++j; else
		if(i%3==2) ++k;
	}
}
int main(){
	freopen("arg.in","r",stdin);
	freopen("arg.out","w",stdout);
	scanf("%d%d",&n,&m); *f=1;
	for(int i=0;i<m;++i) scanf("%d",v+i),p[--v[i]]=i+1;
	for(int i=1;i<=n;++i) d3[i]=d3[i-1]*3;
	for(int i=0,k;i<d3[n];++i) if(f[i]){
		for(int j=*g=k=0;j<n;++j){
			s[j]=(i/d3[j])%3;
			if(s[j]==1) g[++*g]=j;
			if(s[j]) k=max(k,p[j]);
		}
		for(int j=0,t=1;j<n;++j) if(!s[j]){
			while(j>g[t] && t<=*g) ++t;
			if(p[j] && p[j]!=k+1) continue;
			if(t<=*g) f[i+d3[j]+d3[g[t]]]+=f[i];
			else f[i+d3[j]]+=f[i];
		}
	}
	for(int i=0,j,k;i<d3[n];++i){
		cal(i,j,k);
		if(j==m && j+k==n){
			ans+=f[i]; end:;
		}
	}
	printf("%d\n",ans);
}

posted @ 2018-04-17 22:06  扩展的灰(Extended_Ash)  阅读(195)  评论(0编辑  收藏  举报