• <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>

            (首先聲明一下,今年AHOI R1的題==JSOI R3 Day1的題)
            【題解】
            sport:
            首先很容易證明,最優方案必然是左邊都是-,右邊都是+(從樣例也可以看出來囧)……
            因此問題轉化為了第幾次開始+,可以使得開始+的時候,目標元素的位置最左……
            注意到排隊的過程實際上是個冒泡……因此本題的關鍵在于發掘出冒泡的性質囧……
            設最初的序列為A[0..N-1],目標元素為A[pos]。設S[0]為排在A[pos]之左的比A[pos]大的元素的個數(因為一開始排在A[pos]之左的且<=A[pos]的元素,不管腫么搞都一直在目標元素之左,不管它們了囧……),然后,考察所有一開始排在A[pos]之右的,比A[pos]小(注意是<A[pos],不是<=)的所有元素,設它們之間的間隔分別為S[1]、S[2]、S[3]……(具體見圖,其中S[1]為A[pos]和第一個比A[pos]小的元素之間的間隔)

            然后來審視一下整個冒泡的過程。一開始,必然是將目標元素左邊的一個比目標元素大的元素移到右邊去,在目標元素右邊它可能會停下來,但緊接著必然是一個值更大的元素繼續往右移,最終的效果等價于將目標元素左邊的一個比目標元素大的元素“移出去”了(即移到了最右邊的比目標元素小的元素的右邊),也就是S[0]減了1,而S[1]、S[2]……都沒變,這個過程顯然只能持續S[0]次,目標元素左邊比它大的元素就全部移出去,此時目標元素到達了它“可能到達”的最左位置。
            接著,目標元素和右邊第一個比它小的元素(即圖中的A[i1])之間的,比目標元素大的那些元素將被依次的移出去,也就是S[1]不斷減1,而S[2]及后面的都沒變。這一過程會持續S[1]次,目標元素就和A[i1]靠在一起了(注意,在這個過程中,目標元素的位置是不會變的)。
            如果此時繼續冒泡,則目標元素就會和A[i1]交換,右移一個位置,并且就從這次冒泡開始,S[2]不斷減1,減到0為止,接著目標元素和A[i2]交換,右移一個位置,S[3]不斷減1……直到最后一個S[]值也減到0后,目標元素到達它的目標位置。
            也就是,設“第i階段”為S[i]值減少的這個階段,則在第0階段,目標元素會不斷左移,顯然不用+,第1階段,目標元素不動,也不用+,但從第2階段開始,在每階段的第一次冒泡時,目標元素都會向右移一個位置。因此,開始+的時候,必然是第x(x>=2)階段的開始的那次。由于最多只能+ K次,因此可能的最左位置就是滿足N-1-∑(0<=j<x)S[j] <=K的最小的x值減2,加上一開始就在目標元素之左的,且值不大于目標元素的元素個數(它們必然在目標元素之左)。注意特殊情況:x<2(此時當成x=2處理),或者x不存在(全是-)。
            因此,本題就是預處理求出所有S之后,掃一遍得出最小的x值就行了,O(N)。
            當然,二分答案+暴力判斷可以得50,有神犇說二分答案之后可以在O(N)時間內判斷,從而O(NlogN)解決,我太弱了,完全搞不懂囧……(Orz!!)

            mole:
            首先,一個很重要的事實就是,“整個過程中左手所在的位置嚴格小于右手”其實是一個廢條件!!因為如果左右手交叉了,必然是左手試圖去打靠右的一個,而右手試圖去打靠左的一個,此時,讓它們交換,則兩只手移動的距離都變小,方案仍然合法,且更優(我太弱了,當時就是在這里想抽了很久,以至于木有做這題……后來才知道這題很水……真悲劇!!!)
            接下來就變成了一個全局統籌的問題,可以用網絡流解決:每個地鼠i拆成兩個點:入點i'、出點i'',中間連一條容量為1(表示只能打一次),費用為它的得分的邊,兩只手開始的位置也當成有地鼠,只不過得分為0而已。如果某只手在打完第i個地鼠后能接著去打第j個地鼠,就連邊<i'', j'>,容量1,費用0。s往表示兩只手開始的兩個點的入點連一條容量為1,費用為0的邊,每個出點往T連一條容量為1,費用為0的邊。求這個圖的最大費用最大流即可。
            當然,用O(N3)的DP也可以得到至少60分,如果卡的好的話可能有80以上囧……

            bus:
            裸的數據結構題,用一坨Splay Tree維護即可,唯一要注意的就是鏈可以翻轉,因此要rev標記……
            還是不會搞的可以去做ZJOI2012 Day2的某題……
            這題在數據結構題中還是比較好寫的……值得吐槽的是它的對拍很難寫……本沙茶寫正解用了50min,寫對拍用了75min……
            ———————————————————————————————————————————————————
            【總結 && 一些閑扯】
            (1)(Orz @sunzhouyi)
            “要想滾粗:
            0.仔細看錯或者記錯題。
            1.選對不可做題。
            2.思考坑爹題[鑒于‘坑爹’一詞在AH的特殊含義,建議這一點改為“思考廢條件怎么處理”]。
            3.認真寫自己不會的東西。
            4.最后還不寫暴力分。”
            本沙茶中了其中的三點,因此悲劇了……
            (2)熱烈慶祝今年的題目不再坑爹了!!!!!(因為用的是JSOI的題,bus還抄襲了ZJOI……)
            (3)這次的題目……要么是難想、好寫(sport代碼只有1K多),要么是好想、較難寫,但還可以寫的完的(指bus,和BZOJ上最近的那些數據結構相比真是太人道了……)
            (4)要善于發掘題目的本質,特別是一些隱含的最優性質(比如mole的那個廢條件為什么廢);
            (5)遇到想了很久想不出的題,一定要換一個方向去想,因為很可能是一開始的方向疵了;
            (6)對于代碼量較大的題(如數據結構題),到底寫不寫是要看情況的,靈活掌握;

            一些閑扯:
            (1)本沙茶所在的考場幾乎全是神犇,就我一個沙茶……于是被虐傻了……
            (2)比賽時看到對面的一個人在喝“和其正”……瞬間嚇傻了……(話說腫么木有看到喝阿華田的囧……)
            (3)昨天試機的時候,被Atbiter虐了半天,一開始腫么配置都是“找不到答案文件”……后來才發現Atbiter已經改版了,players下面的第一層文件夾應該是考試場數編號(Day1、Day2……);
            (4)試機的時候發現鼠標是壞的,后來才知道這個考場有6個鼠標壞了,3個鍵盤壞了,2個系統時間顯示錯誤……
            (5)總之這次掛慘了,不過前30應該能進,重點是Round2,加油!!我要復仇!!

            posted @ 2013-05-18 17:38 Mato_No1 閱讀(1113) | 評論 (4)編輯 收藏

            原題地址
            這題真是太神犇了……可以讓人完全搞懂數論同余部分的全部內容……

            題解……由于虹貓大神已經在空間里寫得很詳細了,所以就不腫么寫了囧……
            主要說一下一些難想的和容易搞疵的地方:
            (1)中國剩余定理的那個推論(多個同余方程的模數互質,則整個方程組在小于所有模數之積的范圍內的解數等于各個方程解數之積)其實是很強大的,不光對線性同余方程有用,對這種非線性的同余方程也有用,只需要所有方程都滿足:若模數為MOD,則a是解當且僅當(a+MOD)是解……本題顯然滿足,因此,只要在按質因數拆分后求出各個方程的解數,再相乘即可(本沙茶就是這里木有想起來,結果看了虹貓的題解囧……);
            (2)對于余數不為0且和模數不互質的情況要特別注意(這個好像很多標程都疵了,比如虹貓給的標程,不過數據弱,讓它們過了囧),首先必須是余數含p(p為該方程模數的質因數)因子的個數j是a的倍數(也就是余數是p^a的倍數)才能有解,然后,當有解時,轉化為解必須是p^(j/a)的倍數以及x/(p^(j/a))滿足一個模數指數為原來指數減j的方程,這里需要注意,這個新方程的解數乘以p^(j-j/a)才是原來方程的解數!!道理很簡單,因為模數除以了p^j,而x只除以了p^(j/a)……可以用一組數據檢驗:3 330750 6643012,結果是135而不是15;
            (3)原根只能暴力求(不過最小原根都很小,1000以內的所有質數最小原根最大只有19……),但在求的時候有一個小的優化:首先p的原根也是p的任意整數次方的原根,然后求p的原根時,將(p-1)的非自身因數(預先求出)遞減排序,這樣可以比較快地排除不合法解;
            (4)求逆元時一定要注意,如果得到的逆元是負數,要轉化為正數,另外要取模;
            (5)BSGS的時候一定要注意去重,在保留重復元素的情況下即使使用另一種二分查找也會疵的;
            (6)數組不要開小了;

            代碼:
            #include <iostream>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <string.h>
            #include 
            <math.h>
            #include 
            <algorithm>
            using namespace std;
            #define re(i, n) for (int i=0; i<n; i++)
            #define re1(i, n) for (int i=1; i<=n; i++)
            #define re2(i, l, r) for (int i=l; i<r; i++)
            #define re3(i, l, r) for (int i=l; i<=r; i++)
            #define rre(i, n) for (int i=n-1; i>=0; i--)
            #define rre1(i, n) for (int i=n; i>0; i--)
            #define rre2(i, r, l) for (int i=r-1; i>=l; i--)
            #define rre3(i, r, l) for (int i=r; i>=l; i--)
            #define ll long long
            const int MAXN = 110, MAXP = 50010, INF = ~0U >> 2;
            int P_LEN, _P[MAXP + 1], P[MAXP + 1];
            int A, B, M, n, DS[MAXN], DK[MAXN], R[MAXN], KR[MAXP], res;
            struct sss {
                
            int v, No;
                
            bool operator< (sss s0) const {return v < s0.v || v == s0.v && No < s0.No;}
            } Z[MAXP];
            void prepare0()
            {
                P_LEN 
            = 0int v0;
                
            for (int i=2; i<=MAXP; i++) {
                    
            if (!_P[i]) P[P_LEN++= _P[i] = i; v0 = _P[i] <= MAXP / i ? _P[i] : MAXP / i;
                    
            for (int j=0; j<P_LEN && P[j]<=v0; j++) _P[i * P[j]] = P[j];
                }
            }
            void prepare()
            {
                n 
            = 0int M0 = M;
                re(i, P_LEN) 
            if (!(M0 % P[i])) {
                    DS[n] 
            = P[i]; DK[n] = 1; M0 /= P[i]; while (!(M0 % P[i])) {DK[n]++; M0 /= P[i];} n++;
                    
            if (M0 == 1break;
                }
                
            if (M0 > 1) {DS[n] = M0; DK[n++= 1;}
                
            int x;
                re(i, n) {
                    x 
            = 1; re(j, DK[i]) x *= DS[i];
                    R[i] 
            = B % x;
                }
            }
            ll pow0(ll a, 
            int b, ll MOD)
            {
                
            if (b) {ll _ = pow0(a, b >> 1, MOD); _ = _ * _ % MOD; if (b & 1) _ = _ * a % MOD; return _;} else return 1;
            }
            void exgcd(int a, int b, int &x, int &y)
            {
                
            if (b) {
                    
            int _x, _y; exgcd(b, a % b, _x, _y);
                    x 
            = _y; y = _x - a / b * _y;
                } 
            else {x = 1; y = 0;}
            }
            int gcd(int a, int b)
            {
                
            int r = 0while (b) {r = a % b; a = b; b = r;} return a;
            }
            void solve()
            {
                
            int x, y; res = 1;
                re(i, n) 
            if (!R[i]) {
                    
            if (DK[i] < A) x = 1else x = (DK[i] - 1/ A + 1;
                    re2(j, x, DK[i]) res 
            *= DS[i];
                } 
            else if (!(R[i] % DS[i])) {
                    x 
            = 0while (!(R[i] % DS[i])) {R[i] /= DS[i]; x++;}
                    
            if (x % A) {res = 0return;} else {
                        DK[i] 
            -= x; y = x / A;
                        re2(j, y, x) res 
            *= DS[i];
                    }
                }
                
            int phi, m0, m1, KR_len, _r, v0, _left, _right, _mid, T; bool FF;
                re(i, n) 
            if (R[i]) {
                    x 
            = DS[i] - 1; KR_len = 0;
                    
            for (int j=2; j*j<=x; j++if (!(x % j)) {
                        KR[KR_len
            ++= j;
                        
            if (j * j < x) KR[KR_len++= x / j;
                    }
                    KR[KR_len
            ++= 1;
                    re2(j, 
            2, DS[i]) {
                        FF 
            = 1;
                        rre(k, KR_len) {
                            _r 
            = (int) pow0(j, KR[k], DS[i]);
                            
            if (_r == 1) {FF = 0break;}
                        }
                        
            if (FF) {x = j; break;}
                    }
                    phi 
            = DS[i] - 1; re2(j, 1, DK[i]) phi *= DS[i]; v0 = phi / (DS[i] - 1* DS[i];
                    m0 
            = (int) ceil(sqrt(phi) - 1e-10);
                    Z[
            0].v = 1; Z[0].No = 0; re2(j, 1, m0) {Z[j].v = (ll) Z[j - 1].v * x % v0; Z[j].No = j;}
                    _r 
            = (ll) Z[m0 - 1].v * x % v0; sort(Z, Z + m0);
                    m1 
            = 1; re2(j, 1, m0) if (Z[j].v > Z[j - 1].v) Z[m1++= Z[j];
                    exgcd(_r, v0, x, y); 
            if (x < 0) x += v0; y = R[i];
                    re(j, m0) {
                        _left 
            = 0; _right = m1 - 1; T = -1;
                        
            while (_left <= _right) {
                            _mid 
            = _left + _right >> 1;
                            
            if (y == Z[_mid].v) {T = j * m0 + Z[_mid].No; break;}
                            
            else if (y < Z[_mid].v) _right = _mid - 1else _left = _mid + 1;
                        }
                        
            if (T >= 0breakelse y = (ll) y * x % v0;
                    }
                    x 
            = gcd(A, phi); if (T % x) {res = 0break;} else res *= x;
                }
            }
            int main()
            {
                
            int tests;
                scanf(
            "%d"&tests);
                prepare0();
                re(testno, tests) {
                    scanf(
            "%d%d%d"&A, &B, &M); M += M + 1; B %= M;
                    
            if (!A) {
                        
            if (B == 1) res = M; else res = 0;
                    } 
            else {
                        prepare();
                        solve();
                    }
                    printf(
            "%d\n", res);
                }
                
            return 0;
            }

            posted @ 2013-03-15 19:24 Mato_No1 閱讀(1420) | 評論 (2)編輯 收藏

            【1】BZOJ1713
            F[i][j]=max{F[i1][j1] - (sA[i-1]-sA[i1])2 - (sB[j-1]-sB[j1])2} + A[i]*B[j], 0<=i1<i, 0<=j1<j
            對這個式子進行化簡:
            F[i][j]=max{F[i1][j1] - sA[i1]2 + 2*sA[i-1]*sA[i1] - sB[j1]2 + 2*sB[j-1]*sB[j1]}+A[i]*B[j]-sA[i-1]2-sB[j-1]2
            對于一維的情況,很容易處理——中間就是一條直線,然而這是二維的,對于這種2D/2D的DP方程,要優化到O(N2)級別,需要兩步。
            注意到決策式中除了F[i1][j1]外,其它部分都要么只與i1有關,要么只與j1有關。因此,可以把階段i的各個狀態作為一個整體,在之前得出的各個F[i][j]中,對于相同的j,找到對于目前的i,最優的那個決策——注意,對于相同的j1,里面所有與j1有關的東西都可以先不考慮了,只考慮(F[i1][j1] - sA[i1]2 + 2*sA[i-1]*sA[i1])對于目前i的最優決策。這一步可以在這些直線形成的上凸殼中找到,且滿足決策單調性,可以用一個棧處理(斜率優化)。然后,將這些最優決策以j為自變量再組成一些直線,用棧維護它們的上凸殼,對于每個j,找到最優值即可。
            注意事項:在棧中刪除直線的時候,如果之前的最優決策是這個被刪掉的直線,則要將最優決策先置為不存在,然后再插入新直線后設為新直線。另外,要特別注意平行的情況。

            【2】LCIS
            經典問題,利用上面的分步優化思想,很容易用線段樹得到一個O(N2logN)的做法。

            【3】[SCOI2010]股票交易
            F[i][j]=max{F[i-1][j], max{F[i-W-1][j-a]-A*a, F[i-W-1][j+b]+b*B}}, j<=maxP, 1<=a<=maxA, 1<=b<=maxB
            注意對于相同的j,計算方法是一樣的,且是一條直線(由于有范圍所以其實是線段)。
            所以,計算階段i時,可以將(i-W-1)階段所有的決策當成線段插入,這些線段的斜率都相等,因此比較好維護,只需要判斷邊界即可。

            注意,NOI2011的show雖然也符合對于相同的j計算方法一樣,但它就不能優化,因為它的決策是不連續且無規律的,沒有任何幾何性質。因此,它只能用O(N3)的算法計算出所有狀態。

            posted @ 2013-03-03 15:25 Mato_No1 閱讀(1009) | 評論 (0)編輯 收藏

                 摘要: 原題地址寫了幾天終于寫出來了……(顯然,我太弱了,請各位神犇不要鄙視)在有加點的情況下,動態地維護凸包,有以下兩種方法:<1>維護上、下凸殼(本沙茶采用的方法):凸包可以拆成上、下凸殼,對它們分別維護。兩個凸殼均按照下面定義的<關系(即先x增、再y增)排序,注意,兩個凸殼的兩端是相同的,均為整個凸包的最小點與最大點,除兩端外,它們沒有公共定點。以上凸殼為例...  閱讀全文

            posted @ 2013-02-28 18:29 Mato_No1 閱讀(2819) | 評論 (0)編輯 收藏

            RT,
            今天又優化了一下JZPKIL,用上了各種無恥的手段,仍然無法干掉后兩個點,并且BZOJ上的總時間50s也無法實現(后兩個點一個就要20s),
            看來基于組合數的做法由于要枚舉因數,確實不行……
            (注:后兩個點是人工構造的猥瑣數據,所有的N都是若干個小質數之積,因數個數都上千,有的甚至上萬……)

            認輸了……
            Orz @sevenk

            posted @ 2013-02-06 23:26 Mato_No1 閱讀(1196) | 評論 (1)編輯 收藏

            又是一次WC……
            本沙茶這半年來過得真是頹廢啊囧(雖然比上賽季同期好一些)……實力雖然有所提高,但比起別人來說太不值得一提了……
            本次WC又不知道要被多少人(神犇或一般人)虐了……

            三個愿望:
            (1)能聽懂盡可能多的知識點,最好能將自己以前搞不懂的那些都搞懂囧……
            (2)能認識盡可能多的神犇(包括集訓隊的和本屆的),最好能找到一個人以后共勉,這樣就不會再頹廢了囧……
            (3)明年還能去WC囧(nimendongde)……

            posted @ 2013-01-23 22:37 Mato_No1 閱讀(385) | 評論 (0)編輯 收藏

                 摘要: 【先祝賀一下@Jollwish神犇進入CMO國家集訓隊……合肥OI人總算出了個國家集訓隊員(雖然不是OI的)……】最近捉了兩道猥瑣題……都是有關圖中刪去某邊后的最短路徑的問題……核心思想幾乎相同……但是,它們很明顯是綜合題,代碼量太大了(與NOIP2012的drive和block...  閱讀全文

            posted @ 2013-01-19 16:49 Mato_No1 閱讀(2649) | 評論 (0)編輯 收藏

            原題地址
            2013年第一題……紀念一下……

            設F[i][j]表示坐i次電梯到達房間j,最多能到幾樓,則有
            F[i][j]=max{F[i-1][k]+W[k][j]}, 0<=k<n;
            這里W[k][j]要注意,如果不存在從k到j的電梯,W[k][j]應設為-INF。
            這個方程顯然是可以用矩陣乘法來優化的。
            然后,問題就是求出最小的i使得F[i]的狀態中有值>=M的,這個可以二分(每次看當前解與W的(2^K-1)次方的運算結果,若有解則實際不進行這次運算,否則與W的2^K次方運算)……總時間復雜度是O(n3logM)的,對于本題可能要進行一些常數優化才能過(20個點,每個點5個數據,相當于100個點,時限只有40s),反正本沙茶是卡線過的。

            但是,本題有一個細節很重要,必須要說一下(因為本沙茶在這里卡了1h+)……那就是溢出問題……
            F[i][j]的值是有可能超過long long的范圍的,然而如果硬加高精度的話穩T,這時,在進行矩陣乘法(實際是加法)的時候,需要特判一下,如果這個和超過了INF(INF是~0Ull>>2,>1018),就取INF。這樣可能會破壞結合律,但是木有事,因為若兩個加數都是非負數,則不會破壞,若有負數,則一定表示無解(-INF),這個特判一下就行了(若兩個加數之中有負數,則結果取-INF)。

            代碼:
            #include <iostream>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <string.h>
            using namespace std;
            #define re(i, n) for (int i=0; i<n; i++)
            #define re1(i, n) for (int i=1; i<=n; i++)
            #define re2(i, l, r) for (int i=l; i<r; i++)
            #define re3(i, l, r) for (int i=l; i<=r; i++)
            #define rre(i, n) for (int i=n-1; i>=0; i--)
            #define rre1(i, n) for (int i=n; i>0; i--)
            #define rre2(i, r, l) for (int i=r-1; i>=l; i--)
            #define rre3(i, r, l) for (int i=r; i>=l; i--)
            #define ll long long
            const int MAXN = 110, MAXLEN = 61;
            const ll INF = ~0Ull >> 2;
            int n;
            ll M, A[MAXLEN][MAXN][MAXN], W0[MAXN][MAXN], _[MAXN][MAXN], res;
            void mult(ll A0[][MAXN], ll B0[][MAXN])
            {
                re(i, n) re(j, n) _[i][j] 
            = -INF; ll __;
                re(i, n) re(j, n) re(k, n) 
            if (A0[i][k] >= 0 && B0[k][j] >= 0) {
                    __ 
            = A0[i][k] + B0[k][j];
                    
            if (__ > INF) __ = INF;
                    
            if (__ > _[i][j]) _[i][j] = __;
                }
            }
            void prepare()
            {
                re2(i, 
            1, MAXLEN) {
                    mult(A[i 
            - 1], A[i - 1]);
                    re(j, n) re(k, n) A[i][j][k] 
            = _[j][k];
                    mult(A[i], A[
            0]);
                    re(j, n) re(k, n) A[i][j][k] 
            = _[j][k];
                }
            }
            void solve()
            {
                re(i, n) re(j, n) 
            if (i == j) W0[i][j] = 0else W0[i][j] = -INF; bool FF; res = 0;
                rre(i, MAXLEN) {
                    FF 
            = 0; re(j, n) if (A[i][0][j] >= M) {FF = 1break;}
                    
            if (FF) continue;
                    mult(W0, A[i]);
                    FF 
            = 0; re(j, n) if (_[0][j] >= M) {FF = 1break;}
                    
            if (!FF) {
                        re(j, n) re(k, n) W0[j][k] 
            = _[j][k];
                        mult(W0, A[
            0]);
                        re(j, n) re(k, n) W0[j][k] 
            = _[j][k];
                        res 
            += 2ll << i;
                    }
                }
                FF 
            = 0; re(i, n) if (W0[0][i] >= M) {FF = 1break;}
                
            if (!FF) res++;
            }
            int main()
            {
                
            int tests;
                scanf(
            "%d"&tests);
                re(testno, tests) {
                    cin 
            >> n >> M;
                    re(i, n) re(j, n) {scanf(
            "%lld"&A[0][i][j]); if (!A[0][i][j]) A[0][i][j] = -INF;}
                    prepare();
                    solve();
                    cout 
            << res << endl;
                }
                
            return 0;
            }

            posted @ 2013-01-01 15:39 Mato_No1 閱讀(526) | 評論 (0)編輯 收藏

            在線段樹中,一般都不需要刻意保存其左右子結點的下標,而直接由其本身的下標導出,傳統的寫法是:
            根結點:1
            A的左子結點:2A(寫成A<<1)
            A的右子結點:2A+1(寫成(A<<1)+1)
            這種表示法可以表示出整棵線段樹,因為:
            (1)每個結點的子結點的下標都比它大,這樣就不會出現環;
            (2)每個結點的父結點都是唯一的(其本身下標整除2);
            但是,這種表示法有一個弱點:結點的下標是有可能超過2N的,但不會超過4N,因此,為了表示出跨度為N的線段,我們需要開4N的空間,然而,其中只有2N-1個位置是有用的(因為表示跨度為N的線段的線段樹共有(2N-1)個結點),這樣,就有一半的空間被浪費。尤其是這種線段樹推廣到多維的時候——K維線段樹就只有1/2K的位置是有用的,空間利用率非常低。在某些卡空間的場合,它就囧掉了。

            那么,有木有好一點的寫法呢?最好能使空間利用率達到100%——也就是所有結點的下標剛好就是1~(2N-1)!!(0號結點一般作為“哨兵”,不被占用)
            并且,這種寫法要保證僅僅由結點的下標和它表示的線段的左右端點(因為在遍歷線段時,下標和左右端點基本上都是同時知道的),就能得出其子結點的下標,而不需要借助額外的東東(最好mid都不需要算)。
            這種寫法就是——直接將每個結點的DFS遍歷次序當做它的下標!!
            比如,跨度為6的線段樹:

            容易發現,根結點下標為1,下標為A的結點的左子結點下標為(A+1),右子結點下標為A+SZ(A.L)+1,其中SZ(A.L)為A的左子樹大小。
            若A的左右端點為l、r,mid=(l+r)/2(下取整),則A的左子樹所表示的線段為[l, mid],所以SZ(A.L)=(mid-l+1)*2-1=(mid-l)*2+1=((r-l-1)/2(上取整))*2+1
            這樣,A的右子結點下標就是A+((r-l+1)/2(上取整))*2,也就是A加上大于(r-l)的最小的偶數
            寫在代碼里就是:
            int mid=l+r>>1;
            opr(l, mid, A
            +1);
            opr(mid
            +1, r, (r-l&1?A+r-l+1:A+r-l+2));
            或者,借助位運算,可以免去條件判斷:
            int mid=l+r>>1;
            opr(l, mid, A
            +1);
            opr(mid
            +1, r, A+r-l+2-((r^l)&1));
            經測試,后者(使用位運算的)雖然總的運算次數多于前者(使用條件判斷的),但后者比前者快一點點,其原因可能與C語言中的條件運算符速度較慢有關;

            這樣,我們就成功地將線段樹下標的空間利用率提高到了100%!!以后只需要開2N空間就行了囧……
            與傳統表示法相比,這種新式表示法雖然可以節省空間,但時間消耗要更大一些(時間和空間總是矛盾的囧……),因為它在找右子結點的時候需要較多的運算。平均起來,新式表示法比傳統表示法要慢10~15%,對于某些坑爹的數據(對右子結點調用比較多的那種)可能慢得更多。此外,在下放標記的時候,傳統表示法只需要知道結點下標就行了,而新式表示法必須同時知道結點的左右端點,這樣在dm中就需要傳遞三個參數,從而要慢一些,當然,我們可以不用dm,直接在操作里面寫標記下放。

            posted @ 2012-12-01 12:11 Mato_No1 閱讀(3367) | 評論 (2)編輯 收藏

            原題地址
            本沙茶去年曾經用雙線段樹的方法捉了這題(詳見這里),最近重新審視這題發現,借助平衡樹,可以得到更簡單的方法。

            題目大意:
            有一個長度為N的內存條,每個位置的狀態有占用和不占用兩種,有4種操作:
            (1)Reset:清空所有內存(即將所有位置的狀態改為不占用,刪除所有內存塊);
            (2)New x:申請一個新的內存塊,即找到一個長度為x的連續不占用位置區間,將它們標記為占用,若有多個這樣的區間,取最左邊的,若木有輸出Reject New;
            (3)Free x:在已申請的內存塊中,找到包含位置x的并釋放(將該內存塊刪除,同時其占用的所有位置改為不占用),若木有內存塊包含位置x,則輸出Reject Free;
            (4)Get x:找出已申請的內存塊中,左起第x個,并輸出其左端點;若已申請的內存塊數目不足x個,則輸出Reject Get。

            可以發現,每個已經申請的內存塊盡管代表一段區間,但仍然是獨立的單位,因此,可以把內存塊當成結點,用平衡樹維護(關鍵字為內存塊的左端點位置),New操作中內存塊的插入與Free操作中內存塊的刪除均在此平衡樹內進行,Reset操作只需要將整棵樹銷毀即可。
            問題是,在New操作中,需要找到一個長度為x的連續不占用區間,而連續的不占用區間并不是獨立的單位,因此需要使用線段樹維護。在線段樹中,需要維護<1>結點區間內最長連續不占用塊的長度;<2>結點區間左端、右端連續不占用塊的長度(否則無法維護<1>);同時,由于在New操作中需要區間整體改占用,Free操作中又需要區間整體改不占用,所以應當支持整體改值的標記,對于Reset操作,只需要全部位置改不占用即可(不能重新建樹!!);

            這樣,利用一棵平衡樹加一棵線段樹,就可以得到一個很簡單的方法了(代碼量是雙平衡樹或雙線段樹的一半左右);

            這題的啟示是,在解決數據結構統計類題的時候,到底選用什么樣的數據結構,是有講究的,因為它將直接影響到編程復雜度。一般來說,線段樹比平衡樹好寫,但是對本題而言,雙線段樹反而不如平衡樹加線段樹好寫,這是因為對于內存塊使用平衡樹維護比使用線段樹維護更好。在以后做這種題的時候,要多想一下,找到簡便方法。

            posted @ 2012-11-25 14:54 Mato_No1 閱讀(651) | 評論 (0)編輯 收藏

            僅列出標題
            共12頁: 1 2 3 4 5 6 7 8 9 Last 
            久久亚洲AV成人出白浆无码国产| 午夜视频久久久久一区| 99精品久久精品一区二区| 亚洲AV乱码久久精品蜜桃| 国内精品久久久久久99蜜桃| 99热成人精品免费久久| 久久久久久精品成人免费图片| 久久久久亚洲精品天堂| 欧美精品丝袜久久久中文字幕| 久久九九久精品国产免费直播| 99久久精品无码一区二区毛片| 伊人久久国产免费观看视频| 久久精品国产精品青草| 久久久久久久波多野结衣高潮| 久久国产精品-久久精品| 2021国产精品久久精品| 欧美亚洲另类久久综合| 波多野结衣久久精品| 国产成人99久久亚洲综合精品| 久久av无码专区亚洲av桃花岛| 久久无码AV中文出轨人妻| 国产成人久久激情91| 久久精品天天中文字幕人妻| 四虎久久影院| 色婷婷狠狠久久综合五月| 精品久久久久久无码中文野结衣| 亚洲va中文字幕无码久久不卡| 亚洲人成无码网站久久99热国产| 日本免费久久久久久久网站| 国内精品久久久久| 国产精品久久久久aaaa| 久久人妻少妇嫩草AV无码专区| 国产免费久久精品99re丫y| 亚洲а∨天堂久久精品| 亚洲日本va午夜中文字幕久久| 亚洲国产成人久久综合一区77| 日韩久久久久中文字幕人妻 | 国产99久久九九精品无码| 精品久久久久香蕉网| 狠狠色婷婷综合天天久久丁香 | 久久久久久极精品久久久|