[机房测试]矿石

Description

数轴上有 (n) 个区间,以及 (m) 个点。共有 (2^n-1) 种选区间的集合。问有多少种集合,使得集合里的区间的交包含至少一个给定点。

Solution

如果单独考虑每个点来计数的话,会算重。那就要考虑建立一种映射关系。我们考虑将区间按给定点来离散化(注意清掉空区间),然后按左端点排序。这样新的数轴的每一个点都是给定点。我们遍历每个点,动态维护包含该点的区间的集合,并在添加区间的时候计算贡献。例如已经得到了包含 (i-1) 这个点的区间的集合 (S_{i-1}),现在要得到 (S_i)。对于不包含 (i) 这个点的区间直接删去即可。考虑新加入的区间,我们强制选该区间,剩下的随便选,共有 (2^{|S|}) 种方案。这样计数一定不会算重,因为我们强制选了当前编号最大的区间,之前一定没有出现过。

#include<stdio.h>
#include<algorithm>
#include<cassert>
using namespace std;

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

typedef long long ll;

const int N=1e5+7;
const int Mod=998244353;

struct Seg{
    int l,r;
}a[N],c[N];

inline bool Cmpl(const Seg &X,const Seg &Y){return X.l<Y.l;}
inline bool Cmpr(const Seg &X,const Seg &Y){return X.r<Y.r;}

int b[N],n,m,pw[N];

int main(){
//    freopen("A.in","r",stdin);
//    freopen("A.out","w",stdout);
    n=read(),m=read(),pw[0]=1;
    for(int i=1;i<=n;i++)
        a[i].l=read(),a[i].r=read(),pw[i]=2ll*pw[i-1]%Mod;
    for(int i=1;i<=m;i++) b[i]=read();
    sort(b+1,b+1+m); m=unique(b+1,b+1+m)-(b+1);
//    for(int i=1;i<=m;i++) printf("%d ",b[i]); putchar('
');
    for(int i=1;i<=n;i++){
        a[i].l=lower_bound(b+1,b+1+m,a[i].l)-b,
        a[i].r=upper_bound(b+1,b+1+m,a[i].r)-b-1;
    //    assert(a[i].l<=a[i].r);
        c[i]=a[i];
    }
    sort(a+1,a+1+n,Cmpl),sort(c+1,c+1+n,Cmpr);
    int ans=0,now=0,hd=1,tl=1;
    for(int i=1;i<=m;i++){
        while(hd<=n&&c[hd].r<i){
            if(c[hd].l<=c[hd].r) now--; hd++;
        }
        while(tl<=n&&a[tl].l<=i){
            if(a[tl].l<=a[tl].r)
                ans=((ll)ans+pw[now])%Mod,now++;
            tl++;
        }
    }
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/15232533.html