1. 程式人生 > >【省內訓練2018-10-28】網友串

【省內訓練2018-10-28】網友串

【思路要點】

  • 首先,奇數和偶數可以分開處理,最後再將答案卷積得到最終答案。
  • 預處理每一對網友數是否能夠構成混沌串。
  • 我們用三元組 ( 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;
}