CF1327F AND Segments

链接

Description

要求构造满足下列条件的长度为 \(n\) 的序列 \(a\) 的个数:

  • 每个数值域在 \([0, 2 ^ k)\)
  • \(m\) 个限制条件 \(l, r, x\),需要满足 \(a_l\ \text{and}\ a_{l+1} \text{and}\ ... \text{and}\ a_{r} = x\)。 (二进制按位与)

\(n, m \le 5 · 10^5\)

Solution

二进制表示,每个数都是一个 \(k\) 位的 \(01\) 串。按位与限制,不同位互不影响,考虑分别做再乘法原理乘起来。

现在问题转化为了,构造长度为 \(n\)\(01\) 串,有 \(m\) 个限制。

  • 要么为一段区间都是 \(1\)
  • 或是一段区间必须有一个 \(0\)

不妨枚举 \(0\) 的出现位置。

状态设计

\(f_i\) 表示前 \(i\) 个位置,第 \(i\) 个位置为 \(0\)\([1, i]\) 区间内包含的所有区间条件已经满足的方案数。

状态转移

  • 如果第 \(i\) 位必须填 \(1\)(这个用差分 \(O(n)\) 预处理),那么 \(f_i = 0\)。做完这步,第一个限制肯定满足,因为填 \(0\) 的机会全部被否认掉了。
  • 否则,枚举上一个 \(0\) 的位置 \(0 \le j < i\),即 \((j, i)\) 区间全部填 \(1\)\(f_i = \sum f_j\)。为了满足第二个条件,那么 \((j, i)\) 区间必然不能完全包含第二个限制的任何一个区间,即 \(j\) 必须 \(\ge \max(l)\),其中 \(r \le i - 1\) 的。

优化

对于 \(f_i = \sum f_j\)。发现 \(j\) 所取的是一个滑动窗口 \([\max(l), i - 1]\)。即左右端点都是不降的,这样搞一个变量动态维护这个和即可。

初始状态:\(f_0 = 1\)
答案:\(\sum f_{j}^{n}\)(最后一波连续的 \(1\) 里也不能包含第二个限制中的任意区间)

时间复杂度

\(O(kn)\)

Code

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long LL;

const int N = 500005, P = 998244353;

int n, K, m, ans = 1;
int cnt[N], pre[N], f[N];
int L[N], R[N], X[N];

int inline solve(int w) {
	memset(f, 0, sizeof f);
	memset(cnt, 0, sizeof cnt);
	memset(pre, 0, sizeof pre);
	for (int i = 1; i <= m; i++) {
		if (X[i] >> w & 1) cnt[L[i]]++, cnt[R[i] + 1]--;
		else pre[R[i]] = max(pre[R[i]], L[i]);
	}
	for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1];
	f[0] = 1;
	int s = 1, j = 0;
	for (int i = 1; i <= n; i++) {
		if (!cnt[i]) f[i] = s;
		(s += f[i]) %= P;
		while (j < pre[i]) s = ((LL)s - f[j++] + P) % P;
	}
	int res = 0;
	for (int i = j; i <= n; i++) (res += f[i]) %= P;
	return res;
}

int main() { 
	scanf("%d%d%d", &n, &K, &m);
	for (int i = 1; i <= m; i++) scanf("%d%d%d", L + i, R + i, X + i);
	for (int k = 0; k < K; k++) ans = (LL)ans * solve(k) % P;
	printf("%d\n", ans); 
}

猜你喜欢

转载自www.cnblogs.com/dmoransky/p/12730591.html