LOJ 10172 涂抹果酱
题目链接:https://loj.ac/problem/10172
思路:
看到数据范围,考虑状压$dp$。
发现每一格有$3$种情况,考虑用三进制来表示。
枚举所有情况,将可行解与其三进制建立映射关系。
由于第$k$行上下互不影响,我们可以分别进行$dp$,根据乘法原理相乘即可得到答案。
为了优化时间,我们可以先将可互相转移的状态记录下来,$dp$时直接查询即可。
代码:
#include <bits/stdc++.h> const int MAXM = 10050; const int p = 1000000; typedef int intt; #define int long long using namespace std; vector<int> pb; map<int, string> mp; bool c[1050][1050]; int n, m, k, num, ans1, ans2, res, x[MAXM], f[MAXM][50], g[MAXM][50]; void init(int x) { int p = 1, xx = x; string s; s.clear(); do{ s += (x % 3 + '0'); } while(x /= 3); while(s.length() != m) s += '0'; reverse(s.begin(), s.end()); for(int i = 1; i < (int)s.length(); i++) { if(s[i] == s[i - 1]) return ; } pb.push_back(xx); mp[xx] = s; //cout << xx << " " << mp[xx] << endl; } bool check(int x, int y) { string s, t; s = mp[pb[x]]; t = mp[pb[y]]; for(int i = 0; i < m; i++) { if(s[i] == t[i]) return false; } return true; } intt main() { cin >> n >> m >> k; x[0] = -1; for(int i = 1; i <= m; i++) { cin >> x[i]; if(x[i] == x[i - 1]) { cout << "0" << endl; return 0; } res = res * 3 + (x[i] - 1); } for(int i = 0; i < (int)pow(3, m); i++) { if(res == i) num = (int)pb.size(); init(i); } // cout << a[k] << endl << mp[pb[num]] << endl; for(int j = 0; j < (int)pb.size(); j++) { for(int l = 0; l < (int)pb.size(); l++) { c[j][l] = check(j, l); } } f[k][num] = 1; for(int i = k - 1; i >= 1; i--) { for(int j = 0; j < (int)pb.size(); j++) { for(int l = 0; l < (int)pb.size(); l++) { if(c[j][l]) f[i][j] = (f[i][j] + f[i + 1][l]) % p; } } } g[k][num] = 1; for(int i = k + 1; i <= n; i++) { for(int j = 0; j < (int)pb.size(); j++) { for(int l = 0; l < (int)pb.size(); l++) { if(c[j][l]) g[i][j] = (g[i][j] + g[i - 1][l]) % p; } } } for(int i = 0; i < (int)pb.size(); i++) { ans1 = (ans1 + f[1][i]) % p; ans2 = (ans2 + g[n][i]) % p; } cout << (1ll * ans1 * ans2) % p << endl; return 0; }