题目
[AtCoder ARC058C] 和風いろはちゃん / Iroha and Haiku
分析
显然只用存末尾的一段和小于等于 的状态,先深搜一波。
#include <algorithm>
#include <cstdio>
#include <cstring>
int tot;
void Dfs(int sum) {
if (sum > 17)
return;
tot++;
for (int i = 1; i <= 10; i++)
Dfs(sum + i);
}
int main() {
Dfs(0);
printf("%d", tot);
return 0;
}
仅有
种状态!于是 Hash 即可。可以预处理出数组 Next[i][j]
表示在第
种状态后面加一个数
转移到的状态编号,注意要通过pop_front
之类的操作保证状态里面所有数之和小于等于
。
然后状压 DP, 表示前 个位置,状态的编号为 ,是( )否( )出现过合法段的方案数,注意转移的时候判断合法。
错因
- 的转移没有判断当前状态不合法就直接转移,调了一个多小时 = =
代码
用 map
哈希会很慢,所以我写的 vector
哈希。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
const int MAXN = 40;
const int MAXS = 150000;
const int MOD = 1000000007;
inline int Add(int x, const int &y) {
x += y; if (x >= MOD) x -= MOD; return x;
}
const int P = 23;
const int HASH_MOD = 100003;
int N, X, Y, Z, S;
int Dp[MAXN + 5][MAXS +5][2];
std::vector<std::pair<std::vector<int>, int> > Hash[HASH_MOD + 5];
int Key(const std::vector<int> &T) {
int ret = 0;
for (int i = 0; i < int(T.size()); i++)
ret = (ret * P + T[i]) % HASH_MOD;
return ret;
}
int Find(const std::vector<int> &T) {
int pos = Key(T);
for (int i = 0; i < int(Hash[pos].size()); i++)
if (Hash[pos][i].first == T)
return Hash[pos][i].second;
return 0;
}
int Tot;
bool Good[MAXS + 5];
int Next[MAXS + 5][12];
std::vector<int> cur, State[MAXS +5];
void Dfs(int num, int sum) {
if (num > N || sum > X + Y + Z)
return;
State[++Tot] = cur;
Hash[Key(cur)].push_back(std::make_pair(cur, Tot));
if (sum == X + Y + Z) {
int cnt = 0, tmp = 0;
for (int i = 0; i < int(cur.size()); i++) {
tmp += cur[i];
if ((1 << tmp) & S)
cnt++;
}
Good[Tot] = cnt == 3;
return;
}
for (int i = 1; i <= 10; i++) {
cur.push_back(i);
Dfs(num + 1, sum + i);
cur.pop_back();
}
}
int main() {
scanf("%d%d%d%d", &N, &X, &Y, &Z);
S = (1 << X) | (1 << (X + Y)) | (1 << (X + Y + Z));
Dfs(0, 0);
for (int i = 1; i <= Tot; i++) {
for (int j = 1; j <= 10; j++) {
cur = State[i];
cur.push_back(j);
int sum = 0, k = cur.size() - 1;
while (k >= 0 && sum + cur[k] <= X + Y + Z)
sum += cur[k--];
for (int t = 0; t <= k; t++)
cur.erase(cur.begin());
Next[i][j] = Find(cur);
}
}
Dp[0][1][0] = 1;
for (int i = 0; i < N; i++)
for (int j = 1; j <= Tot; j++)
if (Dp[i][j][0] || Dp[i][j][1]) {
for (int k = 1; k <= 10; k++) {
int s = Next[j][k];
Dp[i + 1][s][1] = Add(Dp[i + 1][s][1], Dp[i][j][1]);
if (Good[s])
Dp[i + 1][s][1] = Add(Dp[i + 1][s][1], Dp[i][j][0]);
else
Dp[i + 1][s][0] = Add(Dp[i + 1][s][0], Dp[i][j][0]);
}
}
int Ans = 0;
for (int i = 1; i <= Tot; i++) {
Ans = Add(Ans, Dp[N][i][1]);
}
printf("%d", Ans);
return 0;
}