【dp 状态压缩 单调栈】bzoj3591: 最长上升子序列

奇妙的单调栈状压dp

Description

给出1~n的一个排列的一个最长上升子序列,求原排列可能的种类数。

Input

第一行一个整数n。
第二行一个整数k,表示最长上升子序列的长度。
第三行k个整数,表示这个最长上升子序列。

Output

第一行一个整数,表示原排列可能的种类数。

Sample Input

5
3
1 3 4

Sample Output

11

HINT

【样例说明】

11种排列分别为(1, 3, 2, 5, 4), (1, 3, 5, 2, 4), (1, 3, 5, 4, 2), (1, 5, 3, 2, 4), (1, 5, 3, 4, 2), (2, 1, 3, 5, 4), (2, 1, 5, 3, 4), (2, 5, 1, 3, 4), (5, 1, 3, 2, 4), (5, 1, 3, 4, 2), (5, 2, 1, 3, 4)。

【数据规模和约定】

对于30%的数据,1 <= n <= 11。

对于70%的数据,1 <= n <= 14。

对于100%的数据,1 <= n <= 15,答案小于2^31。


题目分析

这种属于奇奇怪怪的状压dp:把单调栈拿来状压。

考虑求解LIS,是一个使用单调栈的过程。因此按序列位置依次考虑,每一个数有三种状态:0:没有被考虑;1:考虑了,在栈里;2:考虑了,已出栈————自然可以三进制状压表示。

再考虑dp的过程:用$f[i]$表示$i$这个三进制状态下的合法方案数。转移时则从小到大枚举每一个数,看其是否能够通过当前状态合法转移至下一个LIS(注意判断转移前的合法性)。

转移不难,重点在于想到状压每个数对于单调栈的状态。

 

 1 #include<bits/stdc++.h>
 2 const int maxn = 23;
 3 
 4 int val[maxn],base[maxn],n,m;
 5 int num[maxn],pre[maxn];
 6 int LIS[maxn],cnt;
 7 int f[14349003],ans;      //f[]不要忘记开大,表示3^n种状态
 8 
 9 int read()
10 {
11     char ch = getchar();
12     int num = 0;
13     bool fl = 0;
14     for (; !isdigit(ch); ch = getchar())
15         if (ch=='-') fl = 1;
16     for (; isdigit(ch); ch = getchar())
17         num = (num<<1)+(num<<3)+ch-48;
18     if (fl) num = -num;
19     return num;
20 }
21 int main()
22 {
23     n = read(), m = read(), num[0] = 0;
24     for (int i=1; i<=m; i++)
25     {
26         num[i] = read()-1, pre[num[i]] = i-1;
27         if (num[i] < num[i-1]){
28             puts("0");
29             return 0;
30         }
31     }
32     base[0] = 1;
33     for (int i=1; i<=n; i++) base[i] = base[i-1]*3;
34     f[0] = 1;
35     for (int i=0; i<base[n]; i++)
36         if (f[i]){
37             int st = i,done,cnt,ins;
38             done = cnt = ins = 0;
39             for (int j=0; j<n; j++)
40             {
41                 val[j] = st%3, st /= 3;
42                 if (val[j]) done++;
43                 if (val[j]==1) LIS[cnt++] = j;
44             }
45             if (done==n){
46                 ans += f[i];
47                 continue;
48             }
49             for (int j=0; j<n; j++)
50             {
51                 if (val[j]) continue;
52                 if (pre[j]&&!val[num[pre[j]]]) continue;
53                 while (LIS[ins] < j&&ins < cnt) ins++;
54                 if (ins==m) continue;
55                 int nxt = i+base[j];
56                 if (ins < cnt) nxt += base[LIS[ins]];
57                 f[nxt] += f[i];
58             }
59         }
60     printf("%d\n",ans);
61     return 0;
62 }

 

 

 

 

END

posted @ 2018-07-26 19:22  AntiQuality  阅读(764)  评论(0编辑  收藏  举报