矿石

矿石

题目描述

 

众所周知,九条可怜家里有矿。

你可以把可怜家的矿场抽象成一条数轴。可怜家有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;
}
原文地址:https://www.cnblogs.com/liankewei/p/10358768.html