考虑对于一个区间 \([l,r]\),其并值为 \(x\) 会有什么限制:
由于所有数字的每个二进制位都是独立的,我们可以考虑先将所有数每个二进制位的选择算出来,最后累乘即可得到答案。
考虑每个二进制位进行 \(DP\),令 \(f[i]\) 表示前一个 \(0\) 填在第 \(i\) 个数字上的合法方案。
首先,如果 \(\exist t,i\in [l_t,r_t]_1\) (此处区间下标是指要求的类别),那么 \(f[i]\) 为 \(0\),这个我们可以在初始化时搞定。
其次,考虑如何进行转移,由于我们已经经过初始化,相当于自动满足要求一,只需考虑如何满足要求二,进行分类讨论:
对于每一位都这样做,然后累乘 \(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;
}
原文:https://www.cnblogs.com/Arextre/p/13343142.html