/*
線段樹 擴點 擴線 區間覆蓋 好題
題意:給出n條平行於Y軸的線段(y', y'', x),然後3條一組,問有多少組可見線段組。“可見”的定義為,兩條線段能由一條水平先串連但不交於其它的線段。“可見線段組”的定義為該組內的3條線段兩兩可見。
心得:之前看poj_3225,死活看不懂,跳過了。做這道題,它們的離散化方法是一樣的:線段 = 一個閉區間 + 兩個端點。而線段樹是只存點的,為瞭解決這個問題我們可以把這個閉區間也化成一個點,這樣就可以存入線段樹裡了。而線段 * 2這一方法正好可以實現這一點。如,
區間[0,2]離散化後就變成這麼些點:
真實的線段: [0], (0,1), [1], (1,2), [2] ...
化為線段樹裡對應的點: 0 1 2 3 4 ...
當然也可以用poj_2528這樣的方法來做,不過就是要處理的東西多些,麻煩些。poj_2528的解法是應對區間過大時防止MLE/TLE的解法,跟這道題不一樣,這道題的重點是同化區間和點,poj_2528的重點是縮短區間的長度。
*** 偶數點代表點,奇數代表線段,遇到有線段類的題目(用線段樹做)經常要考慮乘以2,表示浮點的線段 ***
思路:
下面摘抄自大牛們的blog:
"能明顯的感覺到是區間覆蓋問題了。但是有一個細節問題,就是中間的水平線不一定經過整點,所以即使這個區間的所有點都被覆蓋,也不能說其就不能穿過一條線,於是,可以將所有線段的長度擴大至2倍,這樣就解決了這個問題。"
"從左至右,一次對每條線段,先進行查詢,看左邊能看見多少條線段,然後進行覆蓋,因為很明顯,如果一條線段能看見另一條線段,那麼這個關係必然是相互的,所以對每條線段,只需要往左看就行了"
"注意:如 範例 中 ,0 2 2,3 4 2這兩條線段,可以看到 2 3之間是沒有被覆蓋的,但是線上段樹中我們看不到這條線段,因為 變成 浮點數了,不能處理,那麼我們可以將 座標 x2,這樣就變成 4 6,中間就多出一個點 5 了,就可以判斷了。。
偶數點代表點,奇數代表線段,遇到有線段類的題目(用線段樹做)經常要考慮乘以2,表示浮點的線段。。。poj 3225這題類似"
"注意:由於線段包括端點,如果這條直線y=c剛好穿過某條線段的端點,則情況會變得有些麻煩.可以採用這種方法來做:將上下縱座標乘2,橫座標不變,改變原來[y,y+1)的節點儲存方式,變為[y,y]式,這樣,就可以簡單地處理端點問題,並且它對於所有情況都有很好的效果.自己畫個圖就明白了."
*/
#include <algorithm>#include <vector>#include <stdio.h>#include <string.h>using namespace std;#define MAXN 8005#define MAX_HIGH 8000 * 2#define debug printf("!\n")#define MID(x,y) (((x)+(y))>>1)#define L(x) ((x)<<1)#define R(x) (((x)<<1)|1)struct Line { int x, y1, y2;} line[MAXN];struct Seg_tree_Node { int l, r, label;} f[MAX_HIGH * 4];bool path[MAXN][MAXN];vector<int> vis[MAXN];int n;void init(){ memset(path, false, sizeof(path)); for(int i = 1; i <= n; i++) vis[i].clear();}void build(int u, int l, int r){ f[u].l = l, f[u].r = r, f[u].label = 0; if(l == r) return ; int mid = MID(l, r); build(L(u), l, mid); build(R(u), mid+1, r);}inline bool cmp(const Line & a, const Line & b){ return a.x < b.x;}void Query(int u, int l, int r, int now){ if(f[u].label == 0) return; else if(f[u].label != -1) { if(path[now][f[u].label]) return ; path[now][f[u].label] = true; vis[now].push_back(f[u].label); return ; } int mid = MID(f[u].l, f[u].r); if(r <= mid) Query(L(u), l, r, now); else if(mid < l) Query(R(u), l, r, now); else { Query(L(u), l, mid, now); Query(R(u), mid+1, r, now); }}void push_down(int u){ f[L(u)].label = f[R(u)].label = f[u].label; f[u].label = -1;}void Update(int u, int l, int r, int now){ if(l == f[u].l && f[u].r == r) { f[u].label = now; return; } if(f[u].label > 0) push_down(u); f[u].label = -1; int mid = MID(f[u].l, f[u].r); if(r <= mid) Update(L(u), l, r, now); else if(mid < l) Update(R(u), l, r, now); else { Update(L(u), l, mid, now); Update(R(u), mid+1, r, now); }}int calc(){ int ans = 0; for(int x = 1; x <= n; x++) for(int Size1 = vis[x].size(), i = 0; i < Size1; i++) { int y = vis[x][i]; for(int Size2 = vis[y].size(), j = 0; j < Size2; j++) { int z = vis[y][j]; if(path[x][z]) ans++; } }// for(int i = 1; i <= n; i++) {// printf("# %d : ", i);// for(int j = 0; j < vis[i].size(); j++) printf("%d ", vis[i][j]);// printf("\n");// } return ans; }int main(){ int cases; scanf("%d", &cases); while(cases--) { scanf("%d", &n); init(); build(1, 0, MAX_HIGH); for(int i = 1; i <= n; i++) { scanf("%d%d%d", &line[i].y1, &line[i].y2, &line[i].x); line[i].y1 <<= 1; line[i].y2 <<= 1; } sort(line+1, line+n+1, cmp); for(int i = 1; i <= n; i++) { Query(1, line[i].y1, line[i].y2, i); Update(1, line[i].y1, line[i].y2, i); } printf("%d\n", calc()); } return 0;}