【省内训练2018-10-28】网友串

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/83589716

【思路要点】

  • 首先,奇数和偶数可以分开处理,最后再将答案卷积得到最终答案。
  • 预处理每一对网友数是否能够构成混沌串。
  • 我们用三元组 ( i , j , s ) (i,j,s) 来描述一个状态,表示处理了前 i i 个数,出现了 j j 个混沌串,并且能与集合 s s 中的串组成混沌串的字符串出现过至少 1 1 次。
  • 一个直观的想法是动态规划,记 d p i , j , s dp_{i,j,s} 表示到达 ( i , j , s ) (i,j,s) 的方案数,利用预处理的结果,我们可以轻松地完成转移,但可能的 s s 似乎很多。但实际上,可能出现的 s s 少之又少,因此直接按照此方式动态规划即可。
  • 时间复杂度 O ( N 2 C n t 2 a i ) O(N^2*Cnt*2^{a_i}) ,其中 C n t Cnt 为合法的 s s 的个数,考虑 a i = 1 , 3 , 5 , 7 a_i=1,3,5,7 时, C n t = 238 Cnt=238 ,考虑 a i = 2 , 4 , 6 a_i=2,4,6 时, C n t = 106 Cnt=106 。另外,即使再将 a i a_i 放大至 8 8 ,乃至是 10 10 C n t Cnt 的值也依然在 2000 2000 左右。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
const int P = 998244353;
typedef long long ll;
typedef bitset <256> info;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
info trans[256];
vector <pair <int, int> > st;
int tot, home[MAXN][256];
int n, Max, a[MAXN], ans[MAXN], f[MAXN], g[MAXN];
unordered_map <info, int> dp[2][MAXN][MAXN];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
bool check(pair <int, int> x, pair <int, int> y) {
	auto bit = [&] (int s, int pos) {
		if (s & (1 << (pos - 1))) return true;
		else return false;
	};
	int len = x.first + y.first;
	if (len & 1) return false;
	int tmp = (x.second << y.first) + y.second;
	for (int i = 1, j = len / 2 + 1; j <= len; i++, j++)
		if (bit(tmp, i) != bit(tmp, j)) return true;
	return false;
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]), chkmax(Max, a[i]);
	for (int i = 1; i <= Max; i++)
	for (int j = 0; j <= (1 << i) - 1; j++) {
		st.emplace_back(i, j);
		home[i][j] = tot++;
	}
	for (int i = 0; i < tot; i++)
	for (int j = 0; j < tot; j++)
		trans[i][j] = check(st[i], st[j]);
	dp[0][0][0][0] = dp[1][0][0][0] = 1;
	for (int p = 1; p <= n; p++)
	for (int q = 0; q <= p; q++) {
		int type = a[p] & 1;
		dp[type ^ 1][p][q] = dp[type ^ 1][p - 1][q];
		for (auto x : dp[type][p - 1][q]) {
			int val = x.second; info now = x.first;
			for (int s = 0; s < (1 << a[p]); s++) {
				int pos = home[a[p]][s];
				update(dp[type][p][q + now[pos]][now | trans[pos]], val);
			}
		}
	}
	for (int i = 0; i <= n; i++) {
		for (auto x : dp[0][n][i])
			update(f[i], x.second);
	}
	for (int i = 0; i <= n; i++) {
		for (auto x : dp[1][n][i])
			update(g[i], x.second);
	}
	for (int i = 0; i <= n; i++)
	for (int j = 0; i + j <= n; j++)
		update(ans[i + j], 1ll * f[i] * g[j] % P);
	for (int i = 0; i <= n; i++)
		printf("%d ", ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/83589716