線段樹主要的優(yōu)勢是在將一個線性的數(shù)組轉(zhuǎn)換成一棵樹,從而減少大量的重復(fù)操作,并且在樹中的節(jié)點保存一些信息,以便更好的統(tǒng)計!它的各個操作的復(fù)雜度都是在logn的,而且我們知道,對于n個節(jié)點來說,最多是只有2n-1個樹的節(jié)點,另外對于一個如果有maxn個節(jié)點,那么我們知道最多要用4*maxn個樹的節(jié)點,這個可以證明
Poj2828 插入隊列,在這個過程中,后一個人可能會覆蓋前一個人,那么如何用線段樹來解呢,我們知道線段樹的葉子節(jié)點代表的就是當(dāng)個節(jié)點的值,這里,我們把樹的節(jié)點表示空位的大小,那么在他插入的時候,然后從后面開始插入,那么如果前面出現(xiàn)重復(fù)的,那么因為空位數(shù)減少了,那么他們也會插在后面,插入的根據(jù)是根據(jù)空位的大俠來的,這個更新可以這樣寫
Void update(int p,int l,int r,int rt)
{
當(dāng)前節(jié)點的空位數(shù)減少1
如果p此時比他左孩子節(jié)點的空位數(shù)大,那么在往有孩子插入,此時空位數(shù)p-tree[mid].v
否則,遍歷左孩子 }
  1 //AC 用了3532MS 2 #include<iostream> 3 #include<cstdio> 4 #define maxn 200005 5 using namespace std; 6 struct tree 7  { 8 int lnode,rnode; 9 int v; 10 }; 11 tree a[maxn*4]; 12 int res[maxn]; 13 int pos[maxn],val[maxn]; 14 int posi; 15 void build(int l,int r,int rt) 16  { 17 a[rt].v=r-l+1; 18 if(l==r) 19 { 20 21 return ; 22 } 23 int mid=(l+r)>>1; 24 build(l,mid,rt<<1); 25 build(mid+1,r,rt<<1|1); 26 } 27 void update(int p,int l,int r,int rt) 28  { 29 a[rt].v--; //該位置的空位數(shù)減少一 30 if(l==r) 31 { 32 posi=l; 33 return ; 34 } 35 int mid=(l+r)>>1; 36 if(p<=a[rt<<1].v) // 37 update(p,l,mid,rt<<1); 38 else 39 update(p-a[rt<<1].v,mid+1,r,rt<<1|1); //這里一定要注意是rt,作為根節(jié)點的值 40 } 41 int main() 42  { 43 int n; 44 while(cin>>n) 45 { 46 for(int i=1;i<=n;i++) 47 //cin>>pos[i]>>val[i]; 48 scanf("%d%d",&pos[i],&val[i]); 49 build(1,n,1); 50 for(int i=n;i>=1;i--) 51 { 52 update(pos[i]+1,1,n,1); 53 res[posi]=val[i]; 54 } 55 for(int i=1;i<=n;i++) 56 { 57 /**//* if(i==1) 58 cout<<res[i]; 59 else 60 cout<<" "<<res[i];*/ 61 printf("%d%c",res[i],(i==n?'\n':' ')); 62 } 63 // cout<<endl; 64 } 65 } 66
Hdoj 1754 要求的是一段區(qū)間的最大值,這個在更新的時候,要不斷更新父子節(jié)點,另外在查詢的時候,其實對于每一次,最后的來的值總是從葉子節(jié)點的來的,只不過利用二分的思想,讓他每次都舍棄了一半的選擇!
 code 1 //hdoj 1754 2 /**//* 3 經(jīng)典的線段樹問題了 4 在一段區(qū)間內(nèi)實施某個操作,然后在查詢,用線段樹可以高效的解決 5 線段樹,可以設(shè)置這樣幾個信息,其中一個保存的是一段區(qū)間的最大值, 6 在更新的過程中,逐次更新 7 */ 8 #include<iostream> 9 #define maxn 20005 10 using namespace std; 11 struct Tree 12  { 13 int lnode,rnode,maxscore; 14 }; 15 Tree tree[maxn*4]; 16 int a[maxn]; 17 int max1; 18 void build(int l,int r,int rt,int pos,int p) 19  { 20 // tree[rt].lnode=l;tree[rt].rnode=r; 21 if(l==r) 22 { 23 tree[rt].maxscore=p; 24 return ; 25 } 26 int mid=(l+r)>>1; 27 if(pos<=mid) 28 build(l,mid,rt<<1,pos,p); 29 else 30 build(mid+1,r,rt<<1|1,pos,p); 31 tree[rt].maxscore=max(tree[rt>>1].maxscore,tree[rt<<1|1].maxscore); 32 } 33 void update(int l,int r,int rt,int pos,int p) 34  { 35 if(l==r) {tree[rt].maxscore=p;return ;} 36 int mid=(l+r)>>1; 37 if(pos<=mid) 38 update(l,mid,rt<<1,pos,p); 39 else 40 update(mid+1,r,rt<<1|1,pos,p); 41 tree[rt].maxscore=max(tree[rt>>1].maxscore,tree[rt<<1|1].maxscore); 42 } 43 int query(int L,int R,int l,int r,int rt) 44  { 45 //詢問這個區(qū)間內(nèi)的最高分?jǐn)?shù) 46 47 if(L<=l&&R>=r) 48 { 49 //說明訪問此時已經(jīng)結(jié)束 50 return tree[rt].maxscore; 51 // return max1; 52 } 53 else 54 { 55 int mid=(l+r)>>1; 56 int _lmax=0,_rmax=0; 57 if(L<=mid) 58 _lmax=query(L,R,l,mid,rt<<1); 59 //_lmax=max(_lmax,query(L,R,1,mid,rt<<1)); 60 else 61 _rmax=query(L,R,mid+1,r,rt<<1|1); 62 max1=max(_lmax,_rmax); 63 // _lmax=max(_lmax,query(L,R,mid+1,r,rt<<1|1)); 64 // return _lmax; 65 } 66 // return _lmax; 67 return max1; 68 } 69 int main() 70  { 71 int num_node,num_query; 72 while(cin>>num_node>>num_query) 73 { 74 //build(1,n,1) 75 //memset(tree,0,sizeof(tree)); 76 for(int i=1;i<=num_node;i++) 77 { cin>>a[i]; 78 build(1,num_node,1,i,a[i]); 79 } 80 81 for(int i=1;i<=num_query;i++) 82 { 83 char ch; 84 int a,b; 85 cin>>ch>>a>>b; 86 if(ch=='Q') 87 { 88 query(a,b,1,num_node,1); 89 cout<<max1<<endl; 90 } 91 else if(ch=='U') 92 { 93 update(1,num_node,1,a,b); 94 } 95 } 96 } 97 } 98
Hdoj1698 hook,這題是用了懶惰標(biāo)記,要求的問題是在一段區(qū)間了進(jìn)行了某一個操作,然后求整個操作后,總共的和是多少
懶惰的標(biāo)記是指如果在更新的過程中,父子節(jié)點已經(jīng)被全部覆蓋,那么作為他的孩子節(jié)點也坐上同樣的標(biāo)記,這個思想是可以用這樣,首先判斷當(dāng)前節(jié)點有沒被完全覆蓋,若是,則進(jìn)行懶惰標(biāo)記,對于他的所有孩子節(jié)點都是如此
 code 1 //初次有一個錯誤 ,用cin時 超了,用scanf就花了 1000ms 2 #include<iostream> 3 using namespace std; 4 #define maxn 100005 5 #define lp(x) (x<<1) 6 #define rp(x) (x<<1|1) 7 int lazy[maxn<<2]; 8 int stick[maxn<<2]; 9 void pushUp(int rt) 10  { 11 stick[rt]=stick[lp(rt)]+stick[rp(rt)]; 12 } 13 void pushDown(int p,int len) 14  { 15 //這個就是懶惰標(biāo)記,思想就是要更新的節(jié)點已經(jīng)被 當(dāng)前的節(jié)點覆蓋,那么不需要往下繼續(xù)覆蓋, 16 //在當(dāng)前節(jié)點記錄下信息, 17 if(lazy[p]) 18 { 19 lazy[lp(p)]=lazy[rp(p)]=lazy[p]; 20 //更新信息 21 stick[lp(p)]=(len-(len>>1))*lazy[lp(p)]; 22 stick[rp(p)]=(len>>1)*lazy[rp(p)]; 23 lazy[p]=0; //這里就不太明白了,也就是說在更新好此時的節(jié)點后,那么把它又標(biāo)記成0,表示應(yīng)經(jīng)算過這一段 24 } 25 } 26 void build(int l,int r,int rt) 27  { 28 //如何建造呢 29 lazy[rt]=0; 30 if(l==r) //說明到了根節(jié)點 31 { 32 stick[rt]=1; 33 return ; 34 } 35 int mid=(l+r)>>1; 36 build(l,mid,lp(rt)); 37 build(mid+1,r,rp(rt)); 38 pushUp(rt); 39 } 40 void update(int L,int R,int z,int l,int r,int rt) 41  { 42 43 if(L<=l&&R>=r) 44 { 45 lazy[rt]=z; 46 stick[rt]=(r-l+1)*z; 47 return ; //這里如果不返回,那么就是錯誤 (1) 48 } 49 pushDown(rt,(r-l+1)); 50 int mid=(l+r)>>1; 51 if(L<=mid) update(L,R,z,l,mid,lp(rt)); 52 if(R>mid) update(L,R,z,mid+1,r,rp(rt)); 53 pushUp(rt); 54 } 55 56 int main() 57  { 58 int T,n,Q; 59 int x,y,z; 60 // cin>>T; 61 scanf("%d",&T); 62 int k=0; 63 while(T--) 64 { 65 66 // cin>>n>>Q; 67 scanf("%d%d",&n,&Q); 68 build(1,n,1); 69 for(int i=1;i<=Q;i++) 70 { 71 72 //cin>>x>>y>>z; 73 scanf("%d%d%d",&x,&y,&z); 74 update(x,y,z,1,n,1); 75 } 76 77 //cout<<stick[1]<<endl; 78 printf("Case %d: The total value of the hook is %d.\n",++k,stick[1]); 79 } 80 system("pause"); 81 } 82
Hdoj 3577這題的大意就是 火車有k個座位,乘客可以買從a到b的車票,給你一些乘客的買票需求,要你求的就是哪些乘客能夠買到票
這題用的是線段樹,我們知道最好的情況是在每一條路線上能夠充分的用上,也就是被完全覆蓋,那么在判段許可的時候,如果此段已經(jīng)被覆蓋過,那么不行!這里有一個問
題就是,因為有多個座位,所以每個座位都有一條路線,然后按照上述的思想進(jìn)行處理即可
問題:第一次wa,是因為這里沒注意更新的區(qū)間應(yīng)該是[a,b-1],因為我到了b站之后,就從這站下來了,然后下一次是可以從b站出發(fā)的
 code 1 //406MS 2 #include<iostream> 3 #include<algorithm> 4 #define maxn 1000010 5 #define lp(x) (x<<1) 6 #define rp(x) (x<<1|1) 7 using namespace std; 8 int lazy[maxn<<2]; 9 int stick[maxn<<2]; 10 //int sum[maxn]; 11 int res[100010]; 12 int max(int a,int b) 13  { 14 return a>b?a:b; 15 } 16 void pushUp(int rt) 17  { 18 stick[rt]=max(stick[lp(rt)],stick[rp(rt)]); //這里也要注意了,隨著題目變化,模板也要變化,不是求和 19 } 20 void pushDown(int p) 21  { 22 //這個就是懶惰標(biāo)記,思想就是要更新的節(jié)點已經(jīng)被 當(dāng)前的節(jié)點覆蓋,那么不需要往下繼續(xù)覆蓋, 23 //在當(dāng)前節(jié)點記錄下信息,lazy[p]標(biāo)記的是當(dāng)前被覆蓋的次數(shù) 24 if(lazy[p]) 25 { 26 // lazy[lp(p)]=lazy[rp(p)]=lazy[p]; 27 //更新信息 28 stick[lp(p)]+=lazy[p]; //表示加上這些覆蓋的次數(shù)的值 29 stick[rp(p)]+=lazy[p]; 30 lazy[lp(p)]+=lazy[p]; 31 lazy[rp(p)]+=lazy[p]; 32 lazy[p]=0; 33 } 34 } 35 void update(int L,int R,int z,int l,int r,int rt) 36  { 37 if(L<=l&&R>=r) 38 { 39 lazy[rt]+=z; 40 stick[rt]+=z; 41 return ; //這里如果不返回,那么就是錯誤 (1) 42 } 43 pushDown(rt); 44 int mid=(l+r)>>1; 45 if(L<=mid) update(L,R,z,l,mid,lp(rt)); 46 if(R>mid) update(L,R,z,mid+1,r,rp(rt)); 47 pushUp(rt); 48 } 49 int query(int L,int R,int l,int r,int rt) 50  { 51 if(L<=l&&R>=r) 52 { 53 return stick[rt]; 54 } 55 pushDown(rt); 56 int ret=0; 57 int mid=(l+r)>>1; 58 if(L<=mid) ret=max(ret,query(L,R,l,mid,lp(rt))); 59 if(R>mid) ret=max(ret,query(L,R,mid+1,r,rp(rt))); 60 //pushUp(rt); //這里不能用pushUp 61 return ret; //這里能夠保證最后的ret就是我們要的ret 62 } 63 int main() 64  { 65 int t; 66 int k,Q,a,b; 67 int nCase=0; 68 cin>>t; 69 while(t--) 70 { 71 cin>>k>>Q; 72 memset(stick,0,sizeof(stick)); 73 memset(lazy,0,sizeof(lazy)); 74 int cnt=0; 75 for(int i=1;i<=Q;i++) 76 { 77 cin>>a>>b; 78 b--; //這里要注意了更新的區(qū)間應(yīng)該是[a,b-1],因為我到了b站之后,就從這站下來了,然后下一次是可以從b站出發(fā)的 79 if(query(a,b,1,1000002,1)<k) //最多是已經(jīng)覆蓋k-1條 80 { 81 res[cnt++]=i; 82 update(a,b,1,1,1000002,1); 83 } 84 } 85 printf("Case %d:\n",++nCase); 86 for(int i=0;i<cnt;i++) 87 printf("%d ",res[i]); 88 printf("\n\n"); 89 } 90 system("pause"); 91 } 92
Poj2528 這一題就是一些海報,要求的是最后沒有被完全覆蓋的海報總數(shù), 那么怎么求呢,一個問題就是如果我們按照從一開始的數(shù)據(jù)進(jìn)行處理的話,那么前一張海報可能會被后一張海報完全覆蓋,那么最后的結(jié)果就無法有效求出,那么我們可以反過來,那就是從最上面的海報開始,那么判斷這段區(qū)間內(nèi)是否已經(jīng)被完全覆蓋過,若是,則不用加1,否則加1
這題先用離散化,將大的數(shù)據(jù)映射到比較小的數(shù)字上面,然后,根據(jù)離散后的數(shù)據(jù)我們可以得出,現(xiàn)在的問題就是,如何求,才能夠去掉被完全覆蓋的區(qū)間,那么這里我們建立一個hash,這個hash存放的值是表示第i個海報,如果查詢的當(dāng)前的節(jié)點的記錄值,在hash表里沒有被記錄過,那么說明此區(qū)間沒有被完全覆蓋,
現(xiàn)在證明這樣做的正確性,首先如果前一張海報被后一張覆蓋的情況,那么由于更新后的cnt[]的域記錄的也是后一張海報的離散化值,那么就不會記錄錯誤
總結(jié)上面的過程,這個題目的總體思路就是輸入數(shù)據(jù)后,離散處理,在針對每一個進(jìn)行更新,最后查詢,代碼待更新。。
|