大厨拥有一个长度为 N 的序列 A,其中的每个元素都是 1 到 K 之间的整数(包括 1 和 K)。现在大厨想要得到到另一个序列 B,它的长度与 A 同为 N,而且每个元素也都是 1 到 K 之间的整数(同样包括 1 和 K)。大厨要求序列 A 和序列 B 的最长公共子序列的长度恰好为 L。请你求出有多少个序列 B 可以满足大厨的要求。由于答案可能非常大,请输出答案对 109 + 7 取模之后的结果。
输入格式
输入数据第一行包含一个整数 T,表示数据组数。接下来是 T 组数据。每组数据的第一行包含三个以空格隔开的整数 N、K,和 L。每组数据的第二行包含 N 个整数 A1, A2, . . . , AN,以空格隔开。
输出格式
对于每组数据,输出一行,包含一个整数,即满足要求的 B 序列的方案数对 109 + 7 取模之后的结果。数据范围• 1 ≤ T ≤ 10• 1 ≤ N, K ≤ 16• 1 ≤ L ≤ N• 序列 A 中的每个元素都是 1 到 K 之间的整数(包括 1 和 K)
Input: 2 2 2 1 1 2 3 3 2 1 2 2 Output: 3 11
第一个样例:满足要求的序列 B 一共三种:[1, 1]、[2, 2],和 [2, 1]。这三个序列与序列 A 的最长公共子序列长度均为 1。注意 [1, 2] 不是一个满足要求的序列,因为它与序列 A 的最长公共子序列长度为 2。第二个样例:满足要求的序列 B 一共 11 种:[1, 1, 2]、[1, 2, 1]、[1, 2, 3]、[1, 3, 2]、[2, 1, 2]、[2, 2, 1]、[2, 2, 2]、[2, 2, 3]、[2, 3, 2]、[3, 1, 2],和 [3, 2, 2]。说明我们认为两个序列不同,当且仅当两个序列有至少一个位置上的元素值不同。
https://www.codechef.com/COOK61/problems/SEQLCS
————————————————————————
是一道不错的难题。next[d][j]表示在构造我们要求的其中一种数列时向末位插入数字j(1<=j<=k)之后最长公共子序列变化。d和mask都是二进制,表示取了A数组的某几位组成最长公共子序列。dp[i][d]表示距离构成B数组还差i位,还差d的二进制表示的几位最长公共子序列。
为了避免重复算,比如1 2 2,避免算两次1 2,或者算两次2,中间那一段难懂的代码就是完成这个操作。所以还是以1 2 2为例,二进制d取3和取5是一样的,取2和取4也是一样的,因此每次都要把这些一样的情况的mask归成一样的,这样在算dp时才会只算一次。
#include <bits/stdc++.h> using namespace std; const int N = 18; const int M = (1 << 16) + 2; const int MOD = 1000000007; int dp[N][M], next[M][N], a[N], oldLCS[N], newLCS[N]; int t, n, k, l, i, j, d, res; int main() { // freopen("input.in", "r", stdin); // freopen("output.out", "w", stdout); scanf("%d", &t); while (t--) { scanf("%d %d %d", &n, &k, &l); for (i = 1; i <= n; ++i) { scanf("%d", a + i); } memset(dp, 0, sizeof dp); for (d = 0; d < (1 << n); ++d) { oldLCS[0] = 0; for (j = 0; j < n; ++j) { oldLCS[j + 1] = oldLCS[j] + ((d >> j) & 1); } for (j = 1; j <= k; ++j) { newLCS[0] = 0; for (i = 1; i <= n; ++i) { if (a[i] == j) { newLCS[i] = oldLCS[i - 1] + 1; } else { newLCS[i] = max(oldLCS[i], newLCS[i - 1]); } } int mask = 0; for (i = 1; i <= n; ++i) { if (newLCS[i] > newLCS[i - 1]) { mask += (1 << (i - 1)); } } next[d][j] = mask; // cout<<"// "<<d<<" "<<j<<" "<<mask<<endl; } } dp[0][0] = 1; for (i = 0; i < n; ++i) { for (d = 0; d < (1 << n); ++d) { if (dp[i][d] > 0) { cout<<i<<" "<<d<<endl; for (j = 1; j <= k; ++j) { dp[i + 1][next[d][j]] += dp[i][d]; dp[i + 1][next[d][j]] %= MOD; } } } } res = 0; for (d = 0; d < (1 << n); ++d) { if (__builtin_popcount(d) == l) { res = res + dp[n][d]; if (res >= MOD) res -= MOD; } } printf("%d\n", res); } return 0; }
#include <bits/stdc++.h> using namespace std; #define pb push_back #define mp make_pair #define REP(i, n) for (int i = 0; i < (int)(n); ++i) typedef long long LL; typedef pair<int, int> PII; int tt; int n, k, l; int a[16], d[1 << 16], nd[1 << 16]; const int MOD = 1e9 + 7; int bc[1 << 16]; inline void modAdd(int &x, int y) { x += y; if (x >= MOD) x -= MOD; } int f[17], g[17]; int t[1 << 16][16]; int main() { //freopen("input.txt", "r", stdin); REP(i, 1 << 16) bc[i] = __builtin_popcount(i); scanf("%d", &tt); REP(test, tt) { scanf("%d%d%d", &n, &k, &l); REP(i, n) scanf("%d", a + i), --a[i]; int MX = 1 << n; memset(d, 0, sizeof d); d[0] = 1; REP(mask, MX) { int c = 0; f[0] = 0; REP(i, n) { if (mask & (1 << i)) ++c; f[i + 1] = c; } REP(j, k) { REP(q, n) { if (a[q] == j) { g[q + 1] = f[q] + 1; } else { g[q + 1] = f[q + 1]; } } for (int q = 1; q < n; ++q) { g[q + 1] = max(g[q + 1], g[q]); } g[0] = 0; int nmask = 0; REP(q, n) if (g[q + 1] > g[q]) { nmask |= 1 << q; } t[mask][j] = nmask; // cout<<"// "<<mask<<" "<<j<<" "<<nmask<<endl; } // cout<<"-----------"<<endl; } REP(times, n) { memset(nd, 0, sizeof nd); REP(mask, MX) if (d[mask]) { REP(j, k) { int nmask = t[mask][j]; modAdd(nd[nmask], d[mask]); } } memcpy(d, nd, sizeof d); } int ans = 0; REP(mask, MX) if (bc[mask] == l) { modAdd(ans, d[mask]); } printf("%d\n", ans); } return 0; }