CF1327F AND Segments

洛谷传送门 CF传送门

感谢同机房大神ql12345指导

Solution

因为位运算每一位之间是互不影响的,所以可以按位考虑然后将每一位的方案数相乘得到最终答案。

对于题目中的限制也可以按位拆成小限制,对于 ((l_i,r_i,x_i)) ,如果 (x_i) 这一位上是 (1) ,那么第 ({l_i}) 个数到第 ({r_i}) 数的这一位也全得为 (1)

但是 (x) 这一位是 (0) 就不好考虑了,可以考虑DP。

先要预处理出来 (pos)(a) 数组,其中 (pos_i)(i) 之前最靠近 (i)(0) 的位置至少要填在 (pos_i)(a_i) 为第 (i) 个数这一位是否强制为 (1)

其中 (pos) 预处理为:如果 (x_i) 这一位为 (0)(pos_{r_i+1}=max(pos_{r_i+1},l_i)) ,因为在 ((l_i,r_i)) 这个区间中至少有一个 (0) ,所以如果要 (r_i+1) 之前全部满足条件,那么至少要在 (l_i) 放一个 (0) 。然后再 (pos_i=max(pos_i,pos_{i-1})) 即可。

(a) 的预处理这里用的是差分,可以做到在 (O(n+m)) 的时间预处理出来。(详情看代码o

预处理完了,说DP。

(f_{i,j}) 表示现在到 (i) 位,其中最后一个 (0) 填在 (j) 且满足条件的方案数,有三种情况的转移:

  1. (j<pos_i) 时,因为无法满足 (pos) 的条件,所以 (f_{i,j}=0)
  2. (pos_ileq j<i) 时,因为第 (i) 位只能填 (1) ,不然填 (0) 那么此时 (j=i) ,所以 (f_{i,j}=f_{i-1,j})
  3. (j=i) 时,如果 (a_i=1) ,那么 (f_{i,j}=0) ,不然 (f_{i,j}=sumlimits_{x=pos_i}^{i-1}f_{i-1,x}) ,因为 (pos_i)(i-1) 的每一种填法都能满足。

我们发现第一维没有什么影响,可以考虑将数组 (f_{i,j}) 简化为 (f_j) ,因为 (pos_i) 是有单调性的,所以可以维护一个 (sum) ,及时更新。

时间复杂度: (O(kcdot(n+m)))

Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;
const int N=5e5+10,mod=998244353;
int n,k,m,l[N],r[N],x[N],f[N];
int pos[N],a[N];//pos表示i之前最后一个0的位置 a是这一位是否强制选1

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

inline void add(int &x,const int y){ x=(x+y>=mod?x+y-mod:x+y);}
inline void sub(int &x,const int y){ x=(x<y?x-y+mod:x-y);}

inline void init(int p){    //p是你按位DP到哪一位了
    for(int i=1;i<=n+1;i++) pos[i]=a[i]=0;
    for(int i=1;i<=m;i++)
        if((x[i]>>p)&1) ++a[l[i]],--a[r[i]+1];
        else pos[r[i]+1]=max(pos[r[i]+1],l[i]);
    for(int i=2;i<=n+1;i++)
        a[i]+=a[i-1],pos[i]=max(pos[i],pos[i-1]);
}

inline void dp(){
    for(int i=0;i<=n+1;i++) f[i]=0;
    f[0]=1;
    int sum=1,l=0;
    for(int i=1;i<=n+1;i++){
        while(l<pos[i]) sub(sum,f[l]),f[l]=0,++l;
        f[i]=a[i]?0:sum,add(sum,f[i]);
    }
}

int main(){
    n=read(),k=read(),m=read();
    for(int i=1;i<=m;i++) l[i]=read(),r[i]=read(),x[i]=read();
    int ans=1;
    for(int p=0;p<k;p++){
        init(p);
        dp();
        ans=1ll*ans*f[n+1]%mod;
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jasony/p/13927428.html