• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            ACM___________________________

            ______________白白の屋
            posts - 182, comments - 102, trackbacks - 0, articles - 0
            <2010年11月>
            31123456
            78910111213
            14151617181920
            21222324252627
            2829301234
            567891011

            常用鏈接

            留言簿(24)

            隨筆分類(332)

            隨筆檔案(182)

            FRIENDS

            搜索

            積分與排名

            最新隨筆

            最新評論

            閱讀排行榜

            評論排行榜


             轉載自 http://www.cnitblog.com/cockerel/archive/2006/09/13/16806.html

            相信對算法設計或者數據結構有一定了解的人對線段樹都不會太陌生。它是能夠在log(MaxLen)時間內完成線段的添加、刪除、查詢等操作。但一般的實現都有點復雜(我自寫的是要遞歸的,比較多行)。而線段樹應用中有一種是專門針對點的。(點樹?)它的實現卻非常簡單。
              這種數據結構有什么用?我們先來考慮一下下面的需求(全部要求在LogN時間內完成):如何知道一個點在一個點集里的大小“排名”?很簡單,開一個點數組,排個序,再二分查找就行了;如何在一個點集內動態增刪點?也很簡單,弄個平衡樹就行了(本來平衡樹比線段樹復雜得多,但自從世界上有了STL set這么個好東東,就……^_^)那如果我既要動態增刪點,也要隨時查詢到一個點的排名呢?那對不起,可能就要出動到我們的“點樹”了。
              其實現原理很簡單:每當增加(或刪除)一個大小為X的點時,就在樹上添加(或刪除)一條(X,MaxLen)的線段(不含端點),當要查詢一個點的排名時,只要看看其上有多少條線段就可以了。針對這一需求,這里有個非常簡單的實現(見以下代碼,十多行,夠短了吧?)其中clear()用于清空點集;add()用于添加一個點;cntLs()返回小于n的點的個數,也就是n的升序排名,類似地cntGt是降序排名。
              這個點樹有什么用呢?其中一個應用時在O(NlogN)時間內求出一個排列的逆序數(http://acm.zju.edu.cn/show_problem.php?pid=1484,你有更好的算法嗎?歡迎交流)方法是每讀到一個數x,就讓逆序數+=cntGt(x);然后再add(x)。
              這個實現還可以進行一些擴展。比如刪除del(int n),只要把add(int n)中的++size換成--size,把a[i/2]++改成a[i/2]--即可。另外還可以通過二分查找功能在O(logN)時間內查到排名第n的點的大小。應該也可以三四行內搞定。

             template < int  N > // 表示可用區間為[0,N),其中N必須是2的冪數;  
             
            class  PointTree {
                
             int  a[ 2 * N];
                
             int  size; 
                
             void  clear() { memset( this , 0 , sizeof ( * this ));}  
                
             void  add( int  n) {
                    
             int  i = N + n;  ++ size; 
                    
             for ( ++ a[i]; i > 1 ; i /= 2 )
                        
             if ( ~ i & 1 ) a[i / 2 ] ++ ;
                }
             
             
                
             int  cntLs( int  n) { // 統計小于  
             
                     int  i = N + n,c = 0  // 若統計小于等于則c=a[i];  
             
                     for (; i > 1 ; i /= 2 
                        
             if (i & 1 ) c += a[i / 2 ];
                     
             return  c;
                }
             

                 
             int  cntGt( int  n)  return  size - a[N + n] - cntLs(n); }  
            }
             

              嗯~~~為了解http://acm.zju.edu.cn/show_problem.php?pid=2425這一題,還是把上述兩個擴展給實現了啦,果然不難:
            (接上)
                
            void del(int n){
                    
            if(!a[n+=N])return;
                    
            --size;
                    
            for(--a[n]; n>1; n/=2)
                        
            if(~n&1)--a[n/2];
                }

                
            /*  解決:求點集中第i小的數(由0數起)
                 *    注意:如果i>=size 返回N-1 
                 
            */
             
                
            int operator[](int n){
                    
            int i=1;
                    
            while(i<N){
                        
            if(n<a[i]) i*=2;
                        
            else n-=a[i], i=i*2+1;
                    }

                    
            return i-N;    
                }
                
            };  
            //附一個測試程序
            #include<iostream.h>
            T
            <8192> t;  
            int main(){
                
            char c; int n;
                
            while(cin>>c){
                       
            if(c=='c') t.clear();
                       
            else{
                           cin
            >>n;
                           
            if(c=='a') t.add(n);
                           
            if(c=='d') t.del(n);
                           
            if(c=='q') cout<<t[n]<<endl;
                       }
                
                }
                
                           
                
            return 0;
            }
              
            求點集里n的個數了!
            int cntEQ(int n){
                
            return a[N+n];
            }

            P.S.:
              在寫完這篇文章一段時間后,我認識了另一種功能上比較類似的數據結構,叫做“樹狀數組”。它們有不少相似之處:
            • 針對點集的處理(添加、刪除、查找);
            • 相似的時空復雜度(logN時間,2N空間);
            • 相似的編程復雜度(都比線段樹簡短得多);

            因此,所有可以用樹狀數組解決的問題都可以用這個“點樹”來解決,另外它還有以下好處:

            • 更直觀的轉移(個人感受,不一定要同意);
            • 同時支持自下而上和自上而下兩種方向的查找和更新,而后者樹狀數組不支持,所以樹狀數組不提供某些功能,比如說O(logN)求點集中第k小數。

            posted on 2006-09-13 19:54 踏雪赤兔 

            久久精品国产亚洲AV香蕉| 久久久青草久久久青草| 青青热久久国产久精品| 久久亚洲国产成人影院网站| 无码任你躁久久久久久老妇App| 国产毛片欧美毛片久久久| 99麻豆久久久国产精品免费| 性高湖久久久久久久久AAAAA| 熟妇人妻久久中文字幕| 久久99国产精一区二区三区| 无码国内精品久久综合88| 韩国无遮挡三级久久| 欧美一区二区久久精品| 99久久99久久精品国产| 97精品国产97久久久久久免费| 久久综合丁香激情久久| 日日噜噜夜夜狠狠久久丁香五月| 久久无码精品一区二区三区| 9191精品国产免费久久| MM131亚洲国产美女久久| 狠狠色噜噜色狠狠狠综合久久| 青青青伊人色综合久久| 综合人妻久久一区二区精品| 久久亚洲精品无码观看不卡| 国产成人99久久亚洲综合精品| 久久综合国产乱子伦精品免费| 久久精品国产99国产精品亚洲| 久久久久亚洲AV无码专区桃色| 国产精品日韩欧美久久综合| 99久久精品费精品国产一区二区| 亚洲va中文字幕无码久久不卡 | 久久久久亚洲av综合波多野结衣| 国产精品免费看久久久| 少妇久久久久久被弄高潮| 国产精品久久久久久久人人看| 久久无码国产| 狠狠色丁香久久婷婷综合_中| 2020国产成人久久精品| 国产激情久久久久久熟女老人| 无码专区久久综合久中文字幕| 亚洲精品美女久久久久99|