矿石
矿石
题目描述
众所周知,九条可怜家里有矿。
你可以把可怜家的矿场抽象成一条数轴。可怜家有nn种矿,第ii种矿可以从[li,ri]的任意位置开采得到。
这个暑假,地理老师给了可怜一个列表:可怜的暑假作业就是收集齐这些矿石。为了保证可怜的安全,可怜的爸爸选定了m个相对安全的采矿点,第ii个采矿点的坐标为ai。可怜只能选择其中一个采矿点开采她需要的矿石。
可怜是一个马虎的女孩子。暑假刚开始没多久,可怜就把老师的列表弄丢了。唯一的线索是,列表上的所有矿石都是可怜家有的:一共有2n−12n−1种可能的列表。
可怜现在想要知道,在所有的可能的任务列表中,有多少种是她能够在某一个安全的采矿点完全收集齐的。
n1e5
solution
考虑每一个采矿点。
记它与上一个矿点相比,少掉的矿石集合为A,多出的集合为B,相同的为C
因为矿石是一段连续的区间,所以A集合再也不会出现了。
C内部的贡献不必再算,只需计算BC间和B的贡献。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 100005
#define ll long long
#define mod 998244353
using namespace std;
int n,m,a[maxn],top,flag[maxn];
ll ans,F[maxn];
struct node{
int id,l,r;
bool operator<(const node &T)const{
return T.r<r;
};
}s[maxn],zh[maxn];
priority_queue<node>q;
bool cmpl(node a,node b){
return a.l<b.l;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d%d",&s[i].l,&s[i].r);
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
sort(s+1,s+n+1,cmpl);
sort(a+1,a+m+1);
F[0]=1;for(int i=1;i<=n;i++)F[i]=(F[i-1]*2)%mod;
for(int i=0;i<n;i++)F[i]--;
int now=1;
for(int i=1;i<=m;i++){
while(!q.empty()){
if(q.top().r<a[i])q.pop();
else break;
}
int A=q.size(),B;
for(;s[now].l<=a[i]&&now<=n;now++){
if(s[now].r>=a[i])q.push(s[now]);
}
B=q.size();
ans=ans+F[B]-F[A];ans%=mod;
}
ans=(ans%mod+mod)%mod;
cout<<ans<<endl;
return 0;
}