题目描述
Description
给出一个长度为 m 的序列 A, 请你求出有多少种 1…n 的排列, 满足 A 是它的一个 LIS.
Input
第一行两个整数 n,m.
接下来一行 m 个整数, 表示 A.
Output
一行一个整数表示答案.
Sample Input
5 3
1 3 4
Sample Output
11
Data Constraint
对于前 30% 的数据, n ≤ 9;
对于前 60% 的数据, n ≤ 12;
对于 100% 的数据, 1 ≤ m ≤ n ≤ 15.
30%
暴力
O(n!)
O
(
n
!
)
枚举,判断长度是否相等且是否包含该串。
考试是特别sb的写了30分
60%
暴力+优化,具体懒得写不明
70(?)%
暴力+奇怪优化,具体不明
100%
考虑状压。
首先要会
O(nlogn)
O
(
n
log
n
)
的LIS(具体百度)
接着可以维护它的辅助数组,可以通过辅助数组来构造出原序列
设F[s]表示第i个数的状态
0表示还未放入
1表示放入且在辅助数组里
2表示放入但未在辅助数组里
不难发现,LIS中每次替换掉第一个大于该数的数
在状压中每次寻找一个0,将其改为1,并将其之后第一个1改为2
(其实就是寻找第一个大于该数的数,并将其替换)
这样做还有一个很蛋疼的问题
要保证给定的序列一定是其中一个LIS
首先长度不能超,当前LIS的长度就是辅助数组中元素个数,就是s中1的个数
其次要保证给出序列一定是其一个子序列,那么保证i-1一定在i前插入就行了
(每次加数之前判断前一位是否加进去)
最后答案即为 Σf[s] Σ f [ s ] (s中数全为1或2)
code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;
int p[16]={1,3,9,27,81,243,729,2187,6561,19683,59049,177147,531441,1594323,4782969,14348907};
int a[16];
int b[16];
int f[14348907];
int n,m,i,j,k,l,len,sum,I,ans;
int main()
{
freopen("arg.in","r",stdin);
freopen("arg.out","w",stdout);
scanf("%d%d",&n,&m);
len=p[n]-1;
scanf("%d",&j);
fo(i,2,m)
scanf("%d",&k),a[k]=j,j=k;
f[0]=1;
fo(i,0,len-1)
if (f[i])
{
j=i,k=1,sum=0;
while (j)
{
b[k]=j%3;
j/=3;
if (b[k++]==1)
sum++;
}
k=0;
fo(j,1,n)
if (b[j]==1)
{
fo(l,k+1,j-1)
if (!b[l])//为0
{
if (!a[l] || (b[a[l]]>0))//限制顺序
{
s
I=i+p[l-1]+p[j-1];
f[I]+=f[i];
}
}
k=j;
}
if (sum<m)//限制长度
{
fo(l,k+1,n)
if (!b[l])
{
if (!a[l] || (b[a[l]]>0))
{
I=i+p[l-1];
f[I]+=f[i];
}
}
}
}
ans=0;
fo(i,1,len)
if (f[i])
{
j=i,k=0;
while (j)
{
if (j%3==0)
break;
j/=3;
k++;
}
if (k==n)
ans+=f[i];
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}