题目
分析
由于每一个二进制位互不影响,因此我们可以按位做.
我们以第一位为例.
如果
x
i
x_i
xi第一位为1,那么
l
i
l_i
li到
r
i
r_i
ri都需要为1.
如果
x
i
x_i
xi第一位为0,那么
l
i
l_i
li到
r
i
r_i
ri至少有一个0.
首先我们找到必须为1的位置有哪些,如果
l
l
l到
r
r
r都为1,那么我们让
l
l
l到
r
r
r这段区间加1. 我们可以用差分,求前缀和来实现.将得到的数组设为cnt.如果cnt[i]>0则 第i位必须为1.
我们先考虑
n
2
n^2
n2的二维dp,之后会优化到
n
n
n
设dp[i][j]表示当前位置为i,上一个放0的位置为j 的方案数.
如果cnt[i]>0,则说明这一位必须放1,那么dp[i][j]=dp[i-1][j].
如果cnt[i]=0,那么既可以放1,也可以放0
放1: dp[i][j]=dp[i-1][j]
放0: dp[i][i]=
∑
j
=
1
i
−
1
d
p
[
i
−
1
]
[
j
]
\sum_{j=1}^{i-1}dp[i-1][j]
∑j=1i−1dp[i−1][j],因为放0,之后最近的0就是i了.
现在我们还有一个条件没有满足:
l
l
l到
r
r
r至少会有一个0,这就表示,r位置的前一个0必须在l之后.
我们需要把dp[r][0]到dp[r][l+1]全部清0,这些方案都不合法.
如何优化到
O
(
n
)
O(n)
O(n) .
我们考虑滚动数组,如果放1,那么滚动数组不会发生任何变化,如果放0,我们只需要让dp[i]等于前缀和.我们用sum记录前缀和即可.
其次关于清零操作,一旦某个位置清零,那么就永远等于0了,所以我们只需要记录清零到了第last位,下次清零从last开始即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int mod =998244353;
const int maxn = 5e5+10;
const int inf = 0x3f3f3f3f;
struct node {
int x,l,r;
}book[maxn];
int a[maxn],n,m,k,pre[maxn];
ll dp[maxn];
ll solve(int t) {
memset(a,0,sizeof(a));
memset(pre,0,sizeof(pre));
memset(dp,0,sizeof(dp));
t=1<<t;
rep(i,1,m) {
if(book[i].x&t) {
a[book[i].l]++;
a[book[i].r+1]--;
}
else {
pre[book[i].r]=max(pre[book[i].r],book[i].l);
}
}
rep(i,1,n) {
a[i]=a[i-1]+a[i];
}
dp[0]=1;
ll sum=1;
int last=0;
rep(i,1,n) {
if(a[i]==0) {
dp[i]=sum;
sum=sum*2%mod;
}
while(last<pre[i]) {
sum=(sum-dp[last]+mod)%mod;
last++;
}
}
return sum;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
cin>>n>>k>>m;
rep(i,1,m) {
cin>>book[i].l>>book[i].r>>book[i].x;
}
ll ans=1;
rep(i,0,k-1) {
ans=ans*solve(i)%mod;
}
cout<<ans<<'\n';
return 0;
}