[CF369E] Valera and Queries - 貪心,樹狀陣列,補集轉化
阿新 • • 發佈:2020-09-11
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; } }