[机房测试]矿石
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('\n');
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);
}