[CF1774G] Segment Covering

先考虑单次询问。

奇偶型问题,考虑到如果不要 \([l,r]\) 的区间都可以覆盖整个区间,那么要了同样可以覆盖,并且奇偶相反直接抵消了。所以我们只统计每个区间是“必要的”的情况。

考虑容斥哪些段没有覆盖到,对于剩下的段可选可不选。这些段一旦包含一个区间,那么这个区间就可选可不选,由于前面的分析,此时的数量为 \(0\)。(从这里可以看出,我们的容斥是极具目标性的)

那么我们就可以考虑 dp 了,设 \(p_i\) 表示以 \(i\) 为右端点的所有区间中不包含任何一个给定区间的最小的左端点。每一次转移就钦定 \([j-1,j]\) 这一段不能包含,转移为:\(f_i=-\sum \limits_{j=p_i}^{i}f_{j-1}\),同时钦定 \([-1,0]\) 是无意义的,但是每一个状态钦定了这个,故给所有状态乘上 \(-1\),即令 \(f_{0}=-1\)

考虑写成前缀和的形式:\(s_i-s_{i-1}=-(s_{i-1}-s_{p_i-2})\),即 \(s_i=s_{p_i-2}\),当然这里的 \(s_{p_i-2}\) 可能比 \(0\) 小,此时等于 \(0\)

考虑处理多次询问,连接 \(i \rightarrow p_i-2\) ,若我们从 $r_i $ 能跳到 \(l-1\),则其 \(s_{r_i}\)\(-1\),否则为 \(0\),最后要求求解 \(s_{r_i}-s_{r_i-1}\),我们分别计算 \(r_i\)\(r_i-1\) 即可。若能建出树来,直接倍增跳即可。

但是这道题的值域很大。幸运的是,\(p_i\) 一定对应了某个区间的 \(l+1\),我们树上需要的点只有询问的 \(r_i,r_i-1\) 原区间的 \(l_i-1\),扫描线求解父亲即可。

(我 tm 离散化错了调了四十分钟)

using namespace std;
#define l first
#define r second
#define N 500005
#define p 998244353
#define pii pair<int,int>
int n,m,sz,id[N*3],go[N*3][22];
pii a[N],b[N];
int jump(int x,int y){
    x=lower_bound(id+1,id+1+sz,x)-id;
    for(int k=21;k>=0;k--)if(go[x][k]!=-100&&id[go[x][k]]>=y)x=go[x][k];
    return id[x]==y?-1:0;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r),id[++sz]=a[i].l-1;
    for(int i=1;i<=m;i++)scanf("%d%d",&b[i].l,&b[i].r),id[++sz]=b[i].r,id[++sz]=b[i].r-1;
    sort(id+1,id+1+sz);sz=unique(id+1,id+1+sz)-id-1;
    sort(a+1,a+1+n,[](const pii &x,const pii &y){return x.r<y.r;});a[n+1].r=2e9+1;
    int mx=-100,pt=1;
    while(pt<=sz&&id[pt]<a[1].r)go[pt++][0]=-100;
    for(int i=1;i<=n;i++){
        mx=max(mx,a[i].l-1);
        while(pt<=sz&&id[pt]<a[i+1].r){
            int d=lower_bound(id+1,id+1+sz,mx)-id;
            go[pt][0]=d;
            ++pt;
        }
    }
    for(int k=1;k<=21;k++)
        for(int i=1;i<=sz;i++)
            go[i][k]=go[i][k-1]==-100?-100:go[go[i][k-1]][k-1];
    for(int i=1;i<=m;i++){
        printf("%d\n",(jump(b[i].r,b[i].l-1)-jump(b[i].r-1,b[i].l-1)+p)%p);
    }
    
}