QOJ 5019 整数

发布时间 2023-09-25 11:44:00作者: zltzlt

QOJ 传送门

考虑从低位向高位 dp,设 \(f_{i, S}\) 为考虑到从低到高第 \(i\) 位,当前每个数超出上界的情况为 \(S\)

转移可以枚举这一位填的数:

  • \(a_j = 0, r_j = 1\),那么这一位一定不会超出上界;
  • \(a_j = 1, r_j = 0\),那么这一位一定会超出上界。
  • 否则情况和之前相同。

容易发现,若 \(r_j = 1\),相当于做按位与,否则是按位或。做 FWT 即可。

code
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 20;
const int maxm = (1 << 18) + 50;
const ll mod = 998244353;

ll c[maxn];
int f[maxm], g[maxm], h[maxm], n, m, b[maxm];

inline void upd(int &x, int y) {
	((x += y) >= mod) && (x -= mod);
}

inline void FWT(int *a, int op, int d) {
	for (int k = 1, t = 0; k < m; k <<= 1, ++t) {
		for (int i = 0; i < m; i += (k << 1)) {
			for (int j = 0; j < k; ++j) {
				if (c[t] & (1LL << d)) {
					upd(a[i + j], op == 1 ? a[i + j + k] : mod - a[i + j + k]);
				} else {
					upd(a[i + j + k], op == 1 ? a[i + j] : mod - a[i + j]);
				}
			}
		}
	}
}

void solve() {
	scanf("%d", &n);
	for (int i = 0; i < n; ++i) {
		scanf("%lld", &c[i]);
	}
	m = (1 << n);
	for (int i = 0, x; i < m; ++i) {
		scanf("%1d", &x);
		b[i] = x;
	}
	h[0] = 1;
	for (int d = 0; d < 60; ++d) {
		for (int i = 0; i < m; ++i) {
			f[i] = h[i];
			g[i] = b[i];
		}
		FWT(f, 1, d);
		FWT(g, 1, d);
		for (int i = 0; i < m; ++i) {
			f[i] = 1LL * f[i] * g[i] % mod;
		}
		FWT(f, -1, d);
		for (int i = 0; i < m; ++i) {
			h[i] = f[i];
		}
	}
	printf("%d\n", h[0]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}