POJ2528 線段樹離散化
阿新 • • 發佈:2019-01-25
題意:在牆上貼海報,海報可以互相覆蓋,問最後可以看見幾張海報思路:這題資料範圍很大,直接搞超時+超記憶體,需要離散化:離散化簡單的來說就是隻取我們需要的值來用,比如說區間[1000,2000],[1990,2012]
我們用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]這些值,所以我只需要1000,1990,2000,2012就夠了,將其分別對映到0,1,2,3,在於複雜度就大大的降下來了所以離散化要儲存所有需要用到的值,排序後,分別對映到1~n,這樣複雜度就會小很多很多而這題的難點在於每個數字其實表示的是一個單位長度(並且一個點),這樣普通的離散化會造成許多錯誤(包括我以前的程式碼 ,poj這題資料奇弱)給出下面兩個簡單的例子應該能體現普通離散化的缺陷:1-10 1-4 5-101-10 1-4 6-10為了解決這種缺陷,我們可以在排序後的陣列上加些處理,比如說[1,2,6,10]如果相鄰數字間距大於1的話,在其中加上任意一個數字,比如加成[1,2,3,6,7,10],然後再做線段樹就好了.
#include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> using namespace std; #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 const int N = 2E5 + 7; int a[N], b[N], x[N], l[N], r[N], y[N]; int res, vis[N]; void pushdown(int rt) { if(a[rt]) { a[rt<<1] = a[rt<<1|1] = a[rt]; a[rt] = 0; } } void build(int l, int r, int rt) { vis[rt] = a[rt] = 0; if(l == r) return ; int mid = (l + r) >> 1; build(lson), build(rson); } void update(int L, int R, int c, int l, int r, int rt) { if(L <= l && r <= R) { a[rt] = c; return ; } int mid = (l + r) >> 1; pushdown(rt); if(L <= mid) update(L,R,c,lson); if(R > mid) update(L,R,c,rson); } void query(int l, int r, int rt) { if(l == r) { if(a[rt]) { if(!vis[a[rt]]) { res ++; vis[a[rt]] = 1; } } return ; } pushdown(rt); int mid = (l + r) >> 1; query(lson), query(rson); } int main() { int T; scanf("%d",&T); int n; while(T --) { int cnt = 0; scanf("%d",&n); for(int i = 1;i <= n;i ++) { scanf("%d%d",&l[i],&r[i]); x[cnt++] = l[i], x[cnt++] = r[i]; } sort(x,x+cnt); cnt = unique(x,x+cnt) - x; int m = 0; for(int i = 1;i < cnt;i ++) { if(x[i] != x[i-1] + 1) { y[++m] = x[i-1]; y[++m] = x[i-1] + 1; } else { y[++m] = x[i-1]; } } y[++m] = x[cnt-1]; build(1, m, 1); // for(int i = 1;i <= m;i ++) printf("%d ",y[i]); for(int i = 1;i <= n;i ++) { int ll = lower_bound(y+1,y+1+m, l[i]) - y; int rr = lower_bound(y+1,y+1+m, r[i]) - y; // printf("%d %d\n",ll,rr); update(ll, rr, i, 1, m, 1); } res = 0; query(1,m,1); printf("%d\n", res); } return 0; }