P r o b l e m \mathrm{Problem} Problem
题目描述
你有三个整数 n , k , m n, k, m n,k,m 以及 m m m 个限制 ( l 1 , r 1 , x 1 ) , ( l 2 , r 2 , x 2 ) , … , ( l m , r m , x m ) (l_1, r_1, x_1), (l_2, r_2, x_2), \ldots, (l_m, r_m, x_m) (l1,r1,x1),(l2,r2,x2),…,(lm,rm,xm)。
计算满足下列条件的,长度为 n n n 的序列 a a a 的个数:
- 对于每个 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n, 0 ≤ a i < 2 k 0 \le a_i \lt 2 ^ k 0≤ai<2k。
- 对于每个 1 ≤ i ≤ m 1 \le i \le m 1≤i≤m,数字的按位与 a [ l i ] and a [ l i + 1 ] and … and a [ r i ] = x i a[l_i] \text{ and } a_[l_i + 1] \text{ and } \ldots \text{ and } a[r_i] = x_i a[li] and a[li+1] and … and a[ri]=xi。
两个序列 a , b a, b a,b 被认为是不同的,当且仅当存在一个位置 i i i 满足 a i ≠ b i a_i \neq b_i ai=bi。
由于答案可能过大,请输出其对 998 244 353 998\ 244\ 353 998 244 353 取模的结果。
输入格式
第一行输入三个整数 n , k , m ( 1 ≤ n ≤ 5 ⋅ 1 0 5 ; 1 ≤ k ≤ 30 ; 0 ≤ m ≤ 5 ⋅ 1 0 5 ) n, k, m ~(1 \le n \le 5 \cdot 10 ^ 5; 1 \le k \le 30; 0 \le m \le 5 \cdot 10 ^ 5)~ n,k,m (1≤n≤5⋅105;1≤k≤30;0≤m≤5⋅105) ,分别表示数组 a a a 的长度, a a a 中元素的值域,以及限制的个数。
接下来 m m m 行,每行描述一个限制 l i , r i , x i ( 1 ≤ l i ≤ r i ≤ n ; 0 ≤ x i < 2 k ) l_i, r_i, x_i ~ (1 \le l_i \le r_i \le n; 0 \le x_i \lt 2 ^ k) li,ri,xi (1≤li≤ri≤n;0≤xi<2k),分别表示限制的线段区间以及按位与值。
输出格式
输出一行一个整数,表示满足条件的序列 a a a 的个数,对 998 244 353 998\ 244\ 353 998 244 353 取模的结果。
说明/提示
你可以在 这里 获得有关按位与的信息。
在一个样例中,合法的序列 a a a 有: [ 3 , 3 , 7 , 6 ] [3, 3, 7, 6] [3,3,7,6], [ 3 , 7 , 7 , 6 ] [3, 7, 7, 6] [3,7,7,6] 以及 [ 7 , 3 , 7 , 6 ] [7, 3, 7, 6] [7,3,7,6]。
S o l u t i o n \mathrm{Solution} Solution
这个题我们首先按位考虑,把问题转化为了:
- 一个长度为 n n n 的 01 01 01 序列,给定两种限定:一种是区间内全部为 1 1 1,另一种是区间内至少有一个 0 0 0,问题有多少个合法的符合符合上述方案。
考虑上述两种方案,显然前者只需要直接赋值即可。
对于第二种限制,发现对于当前节点 i i i 来说,如果选取的时 0 0 0,设上一次选取 0 0 0 的位置为 j j j,那么一定需要保证不存在第二种区间 [ l , r ] [l,r] [l,r]满足 j < l ≤ r < i j<l\le r<i j<l≤r<i。因此我们得到结论:
- 当第 i i i 个位置被钦定为 0 0 0 后,上一个 0 0 0 的位置 j j j 必须大于右端点小于 i i i 的任意左端点。即,对于任意取件 r i < i r_i<i ri<i, j j j 至少为 min { l i + 1 } \min\{l_i+1\} min{li+1}。
由于这是计数题,难以考虑组合数的方法,我们考虑DP。
设 f i f_i fi表示到第 i i i 个位置,且当前位置一定为 0 0 0 的方案数。
- 当 a i = 1 a_i=1 ai=1时,强制 f i = 0 f_i=0 fi=0。
- 找到满足条件的最小 j j j f i = ∑ f j f_i=\sum f_j fi=∑fj
考虑优化,我们发现累加的 j j j 一个连续的区间,我们直接用前缀和优化即可。
现在我们的问题转化为了如何求解 j j j 的限制范围。
很妙的方法就是:
L[r[i]] = L[i];
for i = 1 - n : L[i]= max(L[i-1], L[i]).
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 6e5;
const int P = 998244353;
int n, m, k;
int s[N], Lmax[N], f[N], l[N], r[N], x[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
int calc(int t)
{
for (int i=1;i<=n;++i) s[i] = Lmax[i] = f[i] = 0;
for (int i=1;i<=m;++i) {
if ((x[i] >> t) & 1) s[l[i]] ++, s[r[i] + 1] --;
else Lmax[r[i]] = max(Lmax[r[i]], l[i]);
}
for (int i=1;i<=n+1;++i) {
s[i] += s[i-1];
Lmax[i] = max(Lmax[i-1], Lmax[i]);
}
int sum = 1, j = 0; f[0] = 1;
for (int i=1;i<=n+1;++i)
{
if (s[i] > 0) { f[i] = 0; continue; }
while (j < Lmax[i-1]) sum = (sum - f[j]) % P, j ++;
f[i] = sum, sum = (sum + f[i]) % P;
}
f[n+1] = (f[n+1] % P + P) % P;
return f[n+1];
}
signed main(void)
{
n = read(), k = read(), m = read();
for (int i=1;i<=m;++i) {
l[i] = read();
r[i] = read();
x[i] = read();
}
int res = 1;
for (int i=0;i<k;++i) res = res * calc(i) % P;
cout << res << endl;
return 0;
}