[CF1327F]AND Segments
题目
题解
考虑对于一个区间 \([l,r]\),其并值为 \(x\) 会有什么限制:
- 如果 \(x\) 的第 \(i\) 位为 \(1\),则要求 \([l,r]\) 所有数字的第 \(i\) 位为 \(1\);
- 如果 \(x\) 的第 \(i\) 位为 \(0\),则要求 \([l,r]\) 的所有数字第 \(i\) 位不全为 \(1\);
由于所有数字的每个二进制位都是独立的,我们可以考虑先将所有数每个二进制位的选择算出来,最后累乘即可得到答案。
考虑每个二进制位进行 \(DP\),令 \(f[i]\) 表示前一个 \(0\) 填在第 \(i\) 个数字上的合法方案。
首先,如果 \(\exists t,i\in [l_t,r_t]_1\) (此处区间下标是指要求的类别),那么 \(f[i]\) 为 \(0\),这个我们可以在初始化时搞定。
其次,考虑如何进行转移,由于我们已经经过初始化,相当于自动满足要求一,只需考虑如何满足要求二,进行分类讨论:
- 对于 \([l_t,r_t]_2\),如果 \(r_t\ge i\),由于 \(f[i]\) 的定义,这个要求一定可以被满足(因为我们已经在 \(i\) 填了 \(0\),满足一个区间不全为 \(1\)) ;
- 对于 \([l_t,r_t]_2\),如果 \(r_t<i\),那么我们一定要在 \([l_t,r_t]_2\) 中填一个 \(0\),那么 \(f[i]=\sum_{j=l_t}^{j\le i-1}f[j]\),为什么是到 \(i-1\) 而非 \(r_t\) 呢?因为 \(f[j]\) 已经是所谓的“合法方案”,那么对于 \(j\in [r_t+1,i-1]\) 也是一定满足了 \([l_r,r_t]_2\) 这个区间要求的方案数。
- 由于我们要满足所有的条件二,那么我们就必须在 \(2\) 中的所有 \(l_t\) 中,找到最大的 \(l_t'\),然后求 \(\sum_{j=l_t'}^{i-1}f[j]\),发现这是一个连续区间求和,可以考虑维护一个前缀数组。
对于每一位都这样做,然后累乘 \(f[n]\) 的值即可。
时间复杂度 \(\mathcal O(nk)\)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=5e5;
const int MOD=998244353;
struct require{int l,r;
inline bool operator <(const require rhs)const{
if(r==rhs.r)return l<rhs.l;
return r<rhs.r;
}
};
vector<require>q[35];
vector<require>make[35];
int n,k,m;
inline bool cmp(const require lhs,const require rhs){
if(lhs.l==rhs.l)return lhs.r<rhs.r;
return lhs.l<rhs.l;
}
inline void Init(){
scanf("%d %d %d",&n,&k,&m);
for(int i=1,l,r,s;i<=m;++i){
scanf("%d %d %d",&l,&r,&s);
for(int i=0;i<k;++i){
if((s>>i)&1)make[i].push_back(require{l,r});
else q[i].push_back(require{l,r});
}
}
for(int i=0;i<k;++i)
sort(q[i].begin(),q[i].end()),
sort(make[i].begin(),make[i].end(),cmp);
}
int f[MAXN+5];
int pre[MAXN+5];
inline int calc(const int t){
// memset(f,0,sizeof f);
for(int i=0;i<=n+1;++i)f[i]=0;
int pos=0;
for(int i=0,sz=make[t].size();i<sz;++i){
pos=max(pos,make[t][i].l);
for(;pos<=make[t][i].r;++pos)f[pos]=-1;
}
f[0]=pre[0]=1;
int p=0,maxl=0,sz=q[t].size();//指向要求的最后一个
// printf("Now sz == %d\n",sz);
for(int i=1;i<=n+1;++i){
pre[i]=pre[i-1];
if(f[i]==-1)continue;
while(p<sz && q[t][p].r<i)maxl=max(maxl,q[t][p++].l);
if(maxl==0)f[i]=pre[i-1];
else{
f[i]=pre[i-1]-pre[maxl-1];
if(f[i]<0)f[i]+=MOD;
}
pre[i]+=f[i];
if(pre[i]>=MOD)pre[i]-=MOD;
// printf("f[%d] == %d\n",i,f[i]);
}return f[n+1];
}
int ans=1;
signed main(){
Init();
for(int i=0;i<k;++i){
// printf("calc(%d) == %d\n",i,calc(i));
ans=1ll*ans*calc(i)%MOD;
}
printf("%d\n",ans);
return 0;
}