POJ2528【線段樹經典染色。】
阿新 • • 發佈:2019-02-12
來一點轉的線段樹精髓:
1、 線段樹是二叉樹,且必定是平衡二叉樹,但不一定是完全二叉樹。
2、 對於區間[a,b],令mid=(a+b)/2,則其左子樹為[a,mid],右子樹為[mid+1,b],當a==b時,該區間為線段樹的葉子,無需繼續往下劃分。
3、 線段樹雖然不是完全二叉樹,但是可以用完全二叉樹的方式去構造並存儲它,只是最後一層可能存在某些葉子與葉子之間出現“空葉子”,這個無需理會,同樣給空葉子按順序編號,在遍歷線段樹時當判斷到a==b時就認為到了葉子,“空葉子”永遠也不會遍歷到。
4、 之所以要用完全二叉樹的方式去儲存線段樹,是為了提高在插入線段和搜尋時的效率。用p*2,p*2+1的索引方式檢索p的左右子樹要比指標快得多。
5、線段樹的精髓是,能不往下搜尋,就不要往下搜尋,儘可能利用子樹的根的資訊去獲取整棵子樹的資訊。如果在插入線段或檢索特徵值時,每次都非要搜尋到葉子,還不如直接建一棵普通樹更來得方便。
主動思考,一開始用簡單hash判斷染色,可自己總是扯不上線段樹去。
後來明白了思路,每次判斷染色時便把左右區間修改為i(即第幾次修改..) 【顯然這樣才能利用線段樹的優勢啊】
可離散化又出了點問題,學到一招用二分離散化,自己以前學到的那個離散化就是排好序後分別賦值i++,可距離大於1的時候+1就不好處理了,
順便看ACRush以及別人的各種回憶錄,以及在暑假做的某些題,二分或者二分的思路真的應用非常非常廣,OK!
在我看來線段最精髓的地方就是pushdown,只有在查詢或Update有交集的時候才會更新。。
還有一個很重要的地方就是陣列開的大小,為確保自己在記憶體允許的情況儘量開大。
自己分析的時候難免有錯。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define lson rt<<1,l,m #define rson rt<<1|1,m+1,r const int maxn=11111; int col[maxn*16];//這地方必須乘的係數得萬分注意 int li[maxn<<2]; int ri[maxn<<2]; bool hash[maxn<<2];//.. int ans=0; int x[maxn<<2]; int y[maxn<<2]; void Pushdown(int rt) { if(col[rt]!=-1) { col[rt<<1]=col[rt<<1|1]=col[rt]; col[rt]=-1; } } void update(int ql,int qr,int ch,int rt,int l,int r) { if(ql<=l&&qr>=r) { col[rt]=ch; return; } Pushdown(rt); int m=(l+r)>>1; if(ql<=m) update(ql,qr,ch,lson); if(qr>m) update(ql,qr,ch,rson); } void query(int ql,int qr,int rt,int l,int r) { if(col[rt]!=-1) { if(hash[col[rt]]==false)//曾錯在這地方 一種顏色的統計一次就夠了。 { ans++; hash[col[rt]]=true; } col[rt]=-1; return; } if(l==r) return;//要這一步其實是必須的 特殊情況是考慮分叉 Pushdown(rt);//這一步應該有或沒有都能AC。 int m=(l+r)>>1; if(ql<=m) query(ql,qr,lson); if(qr>m) query(ql,qr,rson); } int Bin(int ans,int x[],int l,int r) { while(l<r) { int m=(l+r)>>1; if(x[m]==ans) return m; if(ans<=x[m]) r=m-1; else l=m+1; } return l; } int main() { int case_num; scanf("%d",&case_num); while(case_num--) { ans=0; memset(col,-1,sizeof(col)); memset(hash,false,sizeof(hash)); int n; scanf("%d",&n); int cnt=1; for(int i=1;i<=n;i++) { scanf("%d%d",&li[i],&ri[i]); x[cnt++]=li[i]; x[cnt++]=ri[i]; } //離散化。 應該又學到一招 不一定靠id 使用二分 sort(x+1,x+cnt); int mm=cnt; for(int i=2;i<cnt;i++) { if((x[i]-x[i-1])>1) x[mm++]=x[i-1]++; } sort(x+1,x+mm); int tt=2; y[1]=x[1]; for(int i=2;i<mm;i++) { if(x[i]!=x[i-1]) y[tt++]=x[i]; } for(int i=1;i<=n;i++) { int l=Bin(li[i],y,1,tt-1); int r=Bin(ri[i],y,1,tt-1); update(l,r,i,1,1,tt-1); } query(1,tt-1,1,1,tt-1); printf("%d\n",ans); } return 0; }