樹狀陣列+貪心
阿新 • • 發佈:2019-02-11
小黑的樹
Description
小H是個怕黑的人,小H的愛慕者小Z斥巨資給小H在宿舍和實驗室之間修路燈。為了滿足小H的審美,小Z規定只能在整數位置修路燈,並且每個整數位置都只能有一個路燈。在小H的認知裡,有m個路段是非常黑的,所以之少要安裝一定數量的路燈。
小H未來將會是一個貼心的女朋友,她想計算出小Z最少會為她安裝多少路燈。
Input
第一行一個整數T(1<=T<=300),表示測試資料的組數對於每組資料:第一行輸入三個整數n,m,k;(1≤n≤1e6,1≤m≤1e5,1≤k≤n)第二行k個不同整數用空格隔開,表示這些位置已開始就有路燈接下來 m 行表示約束條件。第 i 行三個整數 li,ri,ti 表示:第 i 個區間 [li,ri] 至少要安裝 ti 盞路燈 (1≤li≤ri≤n,1≤ti≤n)。
Output
對於每組資料,輸出 Case x: ans。其中 x 表示測試資料編號(從 1 開始)。如果無解,y 為 −1。
Sample Input 1
3 5 1 3 1 3 5 2 3 2 5 2 3 1 3 5 2 3 2 3 5 3 5 2 3 1 3 5 2 3 2 4 5 1
Sample Output 1
Case 1: 1 Case 2: 2 Case 3: 1
題解:首先按照區間右端點進行排序,對於一個區間,先用樹狀陣列查詢當前所需區間有多少燈,然後計算還需要need個燈,我們一定是貪心的從右端點r,找need個空白位置,這時候我們利用set位置當前空白位置下標,每個logn查詢。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 7; struct tree //樹狀陣列 { int c[maxn]; int n; void init(int _n) { n = _n; for(int i = 0; i <= n; ++i) c[i] = 0; } void update(int p, int d) { for(int i = p; i <= n; i += i & -i) c[i] += d; } int query(int p) { int res = 0; for(int i = p; i > 0; i -= i & -i) res += c[i]; return res; } int get(int L, int R) { //查詢L,R有多少個燈 return query(R) - query(L - 1); } }bit; struct node { int st, ed, need; bool operator < (const node & x) const { //按照區間右端點排序 if(ed != x.ed) return ed < x.ed; else return st < x.st; } }a[maxn]; int main() { ios::sync_with_stdio(false), cin.tie(0); int T, Case = 0; int n, m, k; cin >> T; while(T --) { cin >> n >> m >> k; bit.init(++n); set<int> pos; //set維護空白位置 for(int i = 1; i <= n; ++i) pos.insert(i); //初始都是空白可以放燈的位置 for(int i = 1; i <= k; ++i) { int p; cin >> p; bit.update(++p, 1); pos.erase(p); //將已經放燈的位置進行刪除 } bool fg = 1; for(int i = 1; i <= m; ++i) { int l, r, t; cin >> l >> r >> t; a[i].st = l + 1; a[i].ed = r + 1; a[i].need = t; if(t > r - l + 1) fg = 0; //區間需要燈數大於區間長度,無解.(也只有這一種無解的情況) } cout << "Case " << ++Case << ": "; if(!fg) { cout << -1 << '\n'; continue; } sort(a + 1, a + 1 + m); int res = 0; for(int i = 1; i <= m; ++i) { int cur = bit.get(a[i].st, a[i].ed); //當前區間已經有的燈 int cnt = a[i].need - cur; //還需要多少燈 int r = a[i].ed; //我們肯定是貪心從右邊放燈,放燈位置不能超過當前右邊界 // cout << "i = "<< i << " " << cnt << " r = " << r << endl; while(cnt > 0) { -- cnt; auto it = pos.upper_bound(r); //二分找到大於r的第一個位置. -- it; //找到後再對迭代器剪一下,就是小於或等於r的第一個位置 int p = *it; //解引用 if(p > r) -- it; p = *it; bit.update(p, 1); //把這個位置更新成1 pos.erase(it); //把空白位置刪除 res ++; } } cout << res << '\n'; } return 0; } /* 首先總區間長度n只有1e5 首先排序複雜度m * log(m) 放燈最多放n個燈(最多更新n次),放燈時候每次進行更新log(n),即為n*log(n) 總複雜度 O(m*log(m) + n*log(n)) */