Mayor's posters 線段樹+離散化
%%% //感覺博主對於離散的解釋很好,很感謝。 %%% //博主解釋了一下本題離散的特殊性,感謝
如三張海報為:1~10 1~4 6~10 離散化時 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10 第一張海報時:牆的1~4被染為1; 第二張海報時:牆的1~2被染為2 3~4仍為1; 第三張海報時:牆的3~4被染為3, 1~2仍為2。 最終,第一張海報就顯示被完全覆蓋了,於是輸出2,但實際上明顯不是這樣,正確輸出為3。 新的離散方法為:在相差大於1的數間加一個數,例如在上面1 4 6 10中間加5(演算法中實際上1,4之間,6,10之間都新增了數的) X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10 這樣之後,第一次是1,5被染成1;第二次1,2被染成2;第三次4,5被染成3 最終,[1,2]為2,3為1,[4,5]為3,於是輸出正確結果3。 離散化時 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10 第一張海報時:牆的1~4被染為1; 第二張海報時:牆的1~2被染為2 3~4仍為1; 第三張海報時:牆的3~4被染為3, 1~2仍為2。 最終,第一張海報就顯示被完全覆蓋了,於是輸出2,但實際上明顯不是這樣,正確輸出為3。 新的離散方法為:在相差大於1的數間加一個數,例如在上面1 4 6 10中間加5(演算法中實際上1,4之間,6,10之間都新增了數的) X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10 這樣之後,第一次是[1,5]被染成1;第二次[1,2]被染成2;第三次[4,5]被染成3 最終,[1,2]為2,3為1,[4,5]為3,於是輸出正確結果3。
這裡說一下對於程式碼的理解 if (left>mid) update(i * 2 + 1, left, right, val); else if (right <= mid) update(i * 2, left, right, val); else { update(i * 2, left, mid, val); update(i * 2 + 1, mid + 1, right, val); } 這裡目標區間的左端點大於mid,則需要向右節點進行更新(因為右節點的區間左端點更大) 同理當(right<=mid)或者(left<=mid <=right)時也是如此
程式碼是之前從一位dalao那看完後,模仿的,但是之前一直沒有寫這道題目的總結,忘了收藏dalao的部落格,很抱歉.
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct point { int l, r; int mark, sum; }; point tree[10001 * 4 * 4], num[10001 * 4]; int ans, res[10001 * 4], vis[10001 * 4]; void build(int i, int left, int right) { tree[i].l = left, tree[i].r = right; tree[i].mark = tree[i].sum = 0; if (left == right) return; int mid = (left + right) / 2; build(i * 2, left, mid); build(i * 2 + 1, mid + 1, right); } void update(int i, int left, int right, int val) { if (left <= tree[i].l&&tree[i].r <= right) { tree[i].mark = tree[i].sum = val; return; } //等同於lazy if (tree[i].mark) { tree[i * 2].mark = tree[i * 2 + 1].mark = tree[i].mark; tree[i * 2].sum = tree[i * 2 + 1].sum = tree[i].mark; tree[i].mark = 0; } int mid = (tree[i].r + tree[i].l) / 2; //此時的mid取決於i所以要判斷mid>left,right<= 的情況。 if (left>mid) update(i * 2 + 1, left, right, val); else if (right <= mid) update(i * 2, left, right, val); else { update(i * 2, left, mid, val); update(i * 2 + 1, mid + 1, right, val); } } //查詢 int find(int i) { if (tree[i].l == tree[i].r) { if (!vis[tree[i].sum])//沒被計算過 { vis[tree[i].sum] = 1; ++ans; } return ans; } //等同於lazy,即延遲更新 if (tree[i].mark) { tree[i * 2].mark = tree[i * 2 + 1].mark = tree[i].mark; tree[i * 2].sum = tree[i * 2 + 1].sum = tree[i].mark; tree[i].mark = 0; } find(i * 2); find(i * 2 + 1); } int main() { int t, i, n, tn, tn1, powr, powl; scanf("%d", &t); if (t == 0) return 0; while (t--) { res[0] = 0; scanf("%d", &n); for (i = 1; i <= n; i++) { scanf("%d %d", &num[i].l, &num[i].r); if (num[i].l>num[i].r) swap(num[i].l, num[i].r); res[++res[0]] = num[i].l; res[++res[0]] = num[i].r; } sort(res + 1, res + res[0] + 1); //離散化 tn1 = tn = unique(res + 1, res + res[0] + 1) - res;//去重後得到範圍 for (i = 2; i<tn; i++)//插入數 if (res[i] - res[i - 1]>1) res[tn1++] = res[i] - 1; //每個相鄰的差值大於1的時候要插入一個數(就插入x[i]-1好了) res[0] = tn1 - 1;//插入後的資料個數 sort(res + 1, res + res[0] + 1); build(1, 1, res[0]);//以新的區間(對映後的範圍)建樹 for (i = 1; i <= n; i++) { powl = lower_bound(res + 1, res + 1 + res[0], num[i].l) - res; powr = lower_bound(res + 1, res + 1 + res[0], num[i].r) - res; //對映後所在位置,進行資料的更新 update(1, powl, powr, i); } ans = 0; memset(vis, 0, sizeof(vis)); vis[0] = 1; printf("%d\n", find(1)); } return 0; }