Description
有一个
n
n
层的三角形,如图所示:
从顶端开始,每次可以选择向左下走或者向后下走,一直到达底端,形成一条路线。
问有多少种方案画条线路满足:
-
m
m
条线路可以重合但是不能交叉
- 给出条限制,每条限制形如:
Ai,Bi,Ci
A
i
,
B
i
,
C
i
,表示第
Ai
A
i
条路线的第
Bi
B
i
步只能向
Ci
C
i
方向走。
Solution
首先可以想到一个简单的状压DP,用 fi,j f i , j 表示第 i i 条路线用二进制表示位的方案数。但是这样转移是 O(m22n) O ( m 2 2 n ) 的。
我们将题意转化成:有
m
m
个位二进制数(
0
0
表示向左走,表示向右走),对于
i>1
i
>
1
,第
i
i
个数的前位的
1
1
的数量一定大于等于第个数的前
j
j
位的的数量。
我们设
fi,j,S
f
i
,
j
,
S
表示前
i
i
个数的前位已经确定,
S
S
的前位表示已经确定的数,后面的位表示限制,即接下来要填的数一定满足
∀k
∀
k
前
k
k
位中的个数一定大于
S
S
的前位的
1
1
的个数。
考虑如何转移。
1. 当前位是
0
0
,现在填或者
S
S
当前位是,现在填
1
1
,都是直接转移;
2. 当前位是
0
0
,现在填,这时我们可以将限制放宽,将
S
S
中的下一个变成
0
0
。(想一想,为什么)
3. 当前位是
1
1
,不能填。
然后就做完辣~时间复杂度
O(nm2n)
O
(
n
m
2
n
)
。
Code
这个代码的时间复杂度有点假,本来还应该预处理对于每一个二进制数在每一个位置后的第一个 1 1 <script type="math/tex" id="MathJax-Element-101">1</script>的位置,但是由于我写得比较shi,加上这个就MLE了。
/************************************************
* Au: Hany01
* Date: Aug 19th, 2018
* Prob: AGC017F Zigzag
* Email: hany01@foxmail.com
* Inst: Yali High School
************************************************/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define SZ(a) ((int)(a).size())
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia
template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
inline int read() {
static int _, __; static char c_;
for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
return _ * __;
}
const int maxn = 21, maxs = 1 << 20, maxk = maxn * maxn, MOD = 1e9 + 7;
struct Limits { int a, b, c; }lmt[maxk];
inline bool operator < (Limits A, Limits B) { return A.a < B.a; }
int n, m, k, f[2][maxn][maxs], all, cur, Ans, t;
inline void ad(int& x, int y) { if ((x += y) >= MOD) x -= MOD; }
int main()
{
#ifdef hany01
freopen("agc017f.in", "r", stdin);
freopen("agc017f.out", "w", stdout);
#endif
n = read() - 1, m = read(), k = read();
For(i, 1, k) lmt[i].a = read(), lmt[i].b = read(), lmt[i].c = read();
sort(lmt + 1, lmt + 1 + k);
f[0][n][0] = 1, all = 1 << n, cur = 1, t = 0;
For(i, 1, m) {
t ^= 1, Set(f[t], 0);
rep(j, all) f[t][0][j] = f[t ^ 1][n][j];
For(j, 1, n) rep(S, all) {
ad(f[t][j][S], f[t][j - 1][S]);
if (!(S >> (j - 1) & 1)) {
int nS = S | (1 << (j - 1));
For(k, j, n - 1) if (S >> k & 1) { nS ^= (1 << k); break; }
ad(f[t][j][nS], f[t][j - 1][S]);
}
}
while (cur <= k && lmt[cur].a == i) {
rep(S, all) if ((S ^ ((lmt[cur].c) << (lmt[cur].b - 1))) & (1 << (lmt[cur].b - 1))) f[t][n][S] = 0;
++ cur;
}
}
rep(S, all) ad(Ans, f[t][n][S]);
printf("%d\n", Ans);
return 0;
}