[AtCoder ARC058C] 和風いろはちゃん / Iroha and Haiku(Hash + 状压 DP) | 错题本

文章目录

题目

[AtCoder ARC058C] 和風いろはちゃん / Iroha and Haiku

分析

显然只用存末尾的一段和小于等于 17 17 的状态,先深搜一波。

#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;
}

输出
仅有 1 0 5 10^5 种状态!于是 Hash 即可。可以预处理出数组 Next[i][j] 表示在第 i i 种状态后面加一个数 j   ( 1 j 10 ) j\ (1 \leq j \leq 10) 转移到的状态编号,注意要通过pop_front 之类的操作保证状态里面所有数之和小于等于 X + Y + Z X+Y+Z

然后状压 DP, d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0 / 1] 表示前 i i 个位置,状态的编号为 j j ,是( 1 1 )否( 0 0 )出现过合法段的方案数,注意转移的时候判断合法。

错因

  • d p [ i ] [ j ] [ 0 ] dp[i][j][0] 的转移没有判断当前状态不合法就直接转移,调了一个多小时 = =

代码

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;
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/107684341