1. 程式人生 > 實用技巧 >[CF369E] Valera and Queries - 貪心,樹狀陣列,補集轉化

[CF369E] Valera and Queries - 貪心,樹狀陣列,補集轉化

Description

給定 \(n\) 條線段 \([l_i,r_i]\),有 \(m\) 個詢問,每次給定若干個點,問多少線段能至少覆蓋這些點中的一個。

Solution

補集轉化,求多少線段一個點也不能覆蓋。

點將數軸分割成了一個線段集,即求有多少原始線段是包含在詢問線段集中的一個線段中的。

要詢問一個線段包含的原始線段個數,考慮離線處理,用樹狀陣列維護即可。

具體地,按右端點排序後掃描,掃到一個原始線段就在樹狀陣列的左端點位置加一,掃到一個詢問線段就直接掃描部分的字尾和來統計答案(掃描順序保證了右端點順序,字尾和保證了左端點關係,故詢問線段包含了原始線段)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 2000005;

int n,m,cnt,p[N];

struct Segment
{
    int l,r,ans;
    bool operator < (const Segment &obj)
    {
        return r < obj.r;
    }
} seg[N];

vector <Segment> segq;
vector <int> qid[N];
vector <int> segque[N],segsrc[N];

int ar[N]; // index: 1 ~ N
int lowbit(int t)
{
    return t & (-t);
}
void add(int i, int v)
{
    for (; i < N; ar[i] += v, i += lowbit(i));
}
int sum(int i)
{
    if(i==0) return 0;
    int s = 0;
    for (; i > 0; s += ar[i], i -= lowbit(i));
    return s;
}
int query(int l,int r)
{
    return sum(r)-sum(l-1);
}
signed main()
{
    ios::sync_with_stdio(false);

    cin>>n>>m;
    int mx=1e6+1;
    for(int i=1; i<=n; i++)
    {
        int t1,t2;
        cin>>t1>>t2;
        seg[i]={t1,t2,0};
        segsrc[t2].push_back(i);
        mx=max(mx,max(t1,t2));
    }
    for(int i=1; i<=m; i++)
    {
        int cnt,t,last=0;
        cin>>cnt;
        while(cnt--)
        {
            cin>>t;
            if(t-last>1)
            {
                qid[i].push_back(segq.size());
                segque[t-1].push_back(segq.size());
                segq.push_back({last+1,t-1});
            }
            last=t;
        }
        {
            int t=1e6+1;
            if(t-last>1)
            {
                qid[i].push_back(segq.size());
                segque[t-1].push_back(segq.size());
                segq.push_back({last+1,t-1});
            }
            last=t;
        }
    }
    for(int i=1; i<=mx; i++)
    {
        for(int tid:segsrc[i])
        {
            Segment &tmp=seg[tid];
            add(tmp.l, 1);
            //cout<<"modify "<<tmp.l<<endl;
        }
        for(int tid:segque[i])
        {
            Segment &tmp=segq[tid];
            tmp.ans=query(tmp.l,tmp.r);
            //cout<<"tid="<<tid<<" ans="<<tmp.ans<<endl;
        }
    }
    for(int i=1;i<=m;i++)
    {
        int ans=n;
        for(int tid:qid[i])
        {
            //cout<<"calling tid="<<tid<<endl;
            Segment &tmp=segq[tid];
            ans-=tmp.ans;
        }
        cout<<ans<<endl;
    }
}