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

                 摘要: 【背景(神犇不要鄙視)】本沙茶在搜索題目的調試上已經掛過兩次了(NOI2011的game用了2h+木有調出來,還耽誤了繼續想show的時間,結果被擋在了集訓隊門外;NOIP2011的mayan,用暴搜都調了2h+,木有調出來,結果慘掛,全國第200名,如果這題AC了就是全國前50名……),為了防止在接下來的比賽中再在這里掛掉,本沙茶決定好好搞一下這個。【DFS的調試技巧】如...  閱讀全文

            posted @ 2012-03-22 20:49 Mato_No1 閱讀(709) | 評論 (1)編輯 收藏

            轉眼間這個空間已經用了一年了……
            今天又看了一下在這里寫的N多總結,真的覺得自己在最近一年之內提高的不少啊囧……雖然這很多都只是模板……離NOI、CTSC的要求還差很遠……
            不過現在的時間還來得及……只要好好努力的話是還有機會達到神犇水平的……(雖然本沙茶已經比WJMZBMR落后一年了囧)
            向著NOI、CTSC、IOI沖刺啊啊……

            另外:可以看出,這里的大部分總結都是關于數據結構的……確實,本沙茶在最近一年之內主要精力都花在數據結構上……也掌握了不少東東(樹狀數組應用、線段樹應用、S(eg)playtree、SBT、樹的路徑剖分、塊狀數組、動態樹、以及一些用于字符串的數據結構)……但是,本沙茶還有很多弱項,有的甚至幾乎一無所知……后面要調整到這上面來了囧……

            posted @ 2012-03-19 23:13 Mato_No1 閱讀(443) | 評論 (0)編輯 收藏

            剛捉完……這次的題目感覺和前幾次難度差不多啊囧……還木有被虐得太慘(當然,我是沙茶,被虐是必然的)

            krizaljka: 超級大水題;
            eko: 如果真是用裸的二分法(不T)的話,就是超級大水題;
            dna: 水題,從后往前掃描,如果遇到B,就進行一次變換(如果該B位的前一位也是B,則進行整體取反,否則,即該B位的前一位是A或者該B位在最前面,則進行單位取反),可以用一個bool記錄前面目前是否被取反了;
            razbibriga: 水題,直接枚舉四個角的字母就行了,然后在計數的時候,要排除掉同一個字符串被用多次的情況,因此對于2行2列的4個字符串中有首尾字母都相同的要特判一下,具體的特殊情況有點多,這里不列舉了囧;
            blokovi: 神犇題!本沙茶只會暴力;
            poplocavanje: 神犇題!本沙茶只會暴力;

            結果……前4道水題AC了,blokovi竟然搞對了7個點(這……難道貪心是正解?),但是poplocavanje得分比預想的要低了囧(不知是哪里疵了)……總分478,rank17(全國除了ZL外的神犇都木有參加,說明我在沙茶中都是rank16……哭死……)

            posted @ 2012-03-18 01:14 Mato_No1 閱讀(1003) | 評論 (3)編輯 收藏

            【背景(神犇不要鄙視)】
            前段時間,本沙茶在捉神馬題都被完虐的情況下,發現了COCI……一看,發現里面有相當數量的水題,于是就去捉了……結果,本想體驗虐題的感覺,可還是被里面的一些神犇題虐了……我太沙茶了,沒臉見人了囧……

            COCI官網

            2011~2012 #1:
            jabuke: 超級大水題;
            matrix:超級大水題,不過本沙茶一開始看疵題了……
            x3: 水題,直接對每一位單獨考慮即可;
            ples: 水題,裸DP;
            sort: 這個題看上去很不好搞囧……但注意題目里面的這個條件:一開始各極大遞減子序列的長度均為偶數(也就是均>1),這樣,第一次模擬一遍以后,剩下的極大遞減子序列就只有長度為2的了,這時每個數要歸位需要與其后面所有比它小的數都交換一次,所以結果就是第一次模擬的rev執行次數加上第一次模擬之后的逆序對總數;
            skakac: 神犇題,因為涉及比較難的知識點,本沙茶暫時不會搞囧……

            2011~2012 #2:
            najboljih5: 超級大水題;
            okret: 超級大水題,注意特殊情況即可;
            zadaca: 水題,直接因數分解一遍,再查找相同的因數(用哈希),求較小值即可,對于10^9的判定應該很容易的,注意特殊情況;
            kompici: 中等難度,需要用到容斥原理,對于開始的10^6個數,由于本質不同的只有1024個,所以可以壓縮成1024種情況,這樣總的復雜度就是1024*1024了;
            funkcija: 神犇題!!巨神無比的遞推!!這里面涉及到的思想需要慢慢總結;
            raspored: 中等難度,模型轉化后可以發現T是無用的,只需要按照時間遞增的順序執行任務(貪心的經典模型),然后用線段樹維護這個遞增序的和就行了;

            2011~2012 #3:
            digitalna: 超級大水題;
            dhondt: 超級大水題,關鍵在于題意的理解(是把每個派別的選票總數依次除以1到14,得14個結果,然后匯總起來取前14大的結果對應的派別,不是按比例);
            pogodak: 水題,暴力模擬即可;
            robot: 水題,注意二分查找的邊界(比如要找大于等于給定值的最小值,需要特判所有的值都小于給定值的情況);
            place: 超級大水題,裸得不能再裸的模型了;
            traka: 本張試卷的唯一一道不水的題(是個神犇題),首先很容易模型轉化為求F[i]S[i-1]-F[i+1]S[i]的最大值,由于F是個定值且為正,可以除以F[i],變成S[i-1]-(F[i+1]/F[i])*S[i],可以看成直線y=S[i-1]-S[i]*x,當x=F[i+1]/F[i]時的縱坐標,這樣把所有的直線搞出來,維護下凸殼即可(當然本沙茶至今未做過這樣數形結合的題目囧……以后可以搞一個專題);

            2011~2012 #4:
            kino: 超級大水題,貪心就能搞定;
            zima: 水題,線段樹操作,注意細節(本沙茶一開始把下放標記dm()中的mr_opr(LCH(No)),mr_opr寫成dm了……成遞歸調用了……為此查了2h+);
            keks:超級大水題,貪心經典模型,不要管前導0的問題;
            ograda:這個是神犇題了(因為本沙茶總是搞不定啊囧……),首先由于相鄰元素的大小關系以定,絕對值號可以去掉的(本沙茶竟然木有想到這個),然后根據貪心思想,應當盡量把大的和小的交替放置,而且這樣必然能得到可行解(詳細證明見官方題解);
            broj:中等難度,P>=5000時可以直接篩,P<5000時用容斥原理(表面上需要計算2N次,N是小于P的質數總數,其實很多交集都是空集,可以忽略掉,最后剩下的非空集合很少的囧……這也是容斥原理之所以廣泛應用的原因啊囧……)
            kriptogram: 中等難度,首先各個單詞可以映射到Trie里面,變成編號,然后就是類似KMP的搞法了(類似于WC2012 Day1上午講的那道CEOI題目)……本沙茶用官方數據本機測試AC,但交上去RE了兩個點……說是Trie爆了……(本機測試時跟蹤了一下,發現木有爆)主要是這題空間卡得太死(64M),而Trie的空間由于要乘上一個104,所以不能開太大(或許這里可以優化,但本沙茶還不會啊囧……)

            posted @ 2012-03-16 20:56 Mato_No1 閱讀(2056) | 評論 (0)編輯 收藏

            COCI 2011~2012 #2 funkcija
            其思想的巧妙程度以及各種細節的處理難度遠超AHOI2009的cchess(以前本沙茶總以為這種才是最神的遞推題囧……)

            具體的題解以及方法歸納以后再寫,先上代碼:
            #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 = 27, MAXM = 100010, MOD = 1000000007, INF = ~0U >> 2;
            struct edge {
                
            int a, b, pre, next;
            } E[(MAXN 
            << 2+ 1];
            int n, m, A[MAXN][2];
            ll F[MAXN][MAXM], G[MAXN][MAXM], res;
            void init_d()
            {
                re1(i, n) E[i].pre 
            = E[i].next = i; m = n + 1;
            }
            void add_edge(int a, int b)
            {
                E[m].a 
            = a; E[m].b = b; E[m].pre = E[a].pre; E[m].next = a; E[a].pre = m; E[E[m].pre].next = m++;
            }
            void init()
            {
                 scanf(
            "%d"&n);
                 
            char ss0[20], ss1[20];
                 re1(i, n) {
                     scanf(
            "%s %s", ss0, ss1);
                     
            if (ss0[0>= 48 && ss0[0<= 57) A[i][0= atoi(ss0); else A[i][0= 96 - ss0[0];
                     
            if (ss1[0>= 48 && ss1[0<= 57) A[i][1= atoi(ss1); else A[i][1= 96 - ss1[0];
                 }
                 init_d();
                 re1(i, n) {
                     
            if (A[i][0< 0) add_edge(-A[i][0], i);
                     
            if (A[i][1< 0) add_edge(-A[i][1], i);
                 }
            }
            void solve()
            {
                
            int x, y, tmp;
                rre1(i, n) re2(j, 
            1, MAXM) {
                    F[i][j] 
            = 1;
                    
            for (int p=E[i].next; p != i; p=E[p].next) {
                        x 
            = E[p].b;
                        
            if (A[x][0< 0) {
                            
            if (A[x][1>= j) {
                                tmp 
            = G[x][A[x][1]] - G[x][j - 1]; if (tmp < 0) tmp += MOD;
                                F[i][j] 
            = F[i][j] * tmp % MOD; 
                            } 
            else {F[i][j] = 0break;}
                        } 
            else {
                            
            if (A[x][0<= j) {
                                tmp 
            = G[x][j] - G[x][A[x][0- 1]; if (tmp < 0) tmp += MOD;
                                F[i][j] 
            = F[i][j] * tmp % MOD;
                            } 
            else {F[i][j] = 0break;}
                        }
                    }
                    G[i][j] 
            = (G[i][j - 1+ F[i][j]) % MOD;
                }
                res 
            = 1;
                re1(i, n) 
            if (A[i][0> 0 && A[i][1> 0) {
                    tmp 
            = G[i][A[i][1]] - G[i][A[i][0- 1]; if (tmp < 0) tmp += MOD;
                    res 
            = res * tmp % MOD;
                }
            }
            void pri()
            {
                 cout 
            << res << endl;
            }
            int main()
            {
                init();
                solve();
                pri();
                
            return 0;
            }

            posted @ 2012-03-12 23:30 Mato_No1 閱讀(507) | 評論 (0)編輯 收藏

            原題地址
            說實話我第一次嘗試寫炮兵陣地是在2009年……已經過去兩年多了,終于找到了一個好的解法……慶祝一下……

            【狀態壓縮DP】
            所謂狀態壓縮DP,就是對于某些DP問題,每一階段的狀態都有很多維,要利用某些手段將它們壓縮到一維(一個正整數),順便進行狀態的精簡(去掉不合法的狀態),然后再進行DP。這里講的主要是傳統的狀壓DP,有一種基于“插頭”的DP,更高級,以后再搞。
            對于本題,可以設計出一個這樣的狀態:[0..1][0..1][0..1]...[0..1](有M個[0..1]),表示該行的每個格子放不放炮兵,如果放,為1,否則為0。顯然,這是一個M位二進制數,如果能把它們壓縮成一個int就好了。

            【如何壓縮】
            第一個問題是這么多維的狀態如何壓縮的問題。
            對于本題,由于是二進制數,直接壓縮就可以了。但是對于某些情況,狀態是個三進制數(每個格子的屬性都有3種)甚至更多進制數,這樣,直接壓縮會導致無法使用位運算,從而使“解壓”變得很麻煩,耗時也很長(需要暴力),因此,可以將每位三進制都拆成兩位二進制:
            0->00
            1->01
            2->10
            (當然1拆成10、2拆成11也木有問題,只要能區分開就行了)
            這樣,每個狀態就可以用2個二進制數來表示,可以在構造出所有合法狀態以后將每個狀態所對應的兩位二進制數存到struct里面,使用時調出即可。

            【如何精簡】
            這個問題是最重要的,因為,如果不精簡,在枚舉狀態以及轉移的時候就會枚舉到很多不合法狀態,導致時間浪費。
            所謂精簡,是指在預處理以及DP過程中,盡量避開不合法狀態。
            (1)預處理中的精簡:
            包括3個部分:
            <1>找到所有可能的合法狀態并編號:根據題意限制,有的狀態在階段內就不合法(比如本題,一行一階段,那么凡是有兩個1位的距離小于2的狀態都不合法),而且這種狀態所占的比重往往還很大(本題中,M=10時,也只有60種可能的合法狀態),此時,為了找到這些合法狀態,可以DFS構造實現。
            需要注意的是,有的題不光要找到一個階段內的合法狀態,還要找到兩個或兩個以上階段內的合法狀態(比如那個有關多米諾骨牌的題),此時需要兩個int同時DFS;
            在找到合法狀態以后,需要對每個合法狀態進行編號,以達到“壓縮”的目的。這里就涉及到了狀態編號和狀態表示的問題:比如,狀態1001,表示為9,在DFS中第一個被搜到,因此編號為0,不要搞混了這兩個(尤其不要搞混“編號為0”和“狀態表示為0”,它們是不同的)。在預處理和DP的過程中,所有涉及到狀態的數組下標,全部是編號而不是表示,知道編號要求表示,可以在DFS中記錄的數組里面調,而知道表示要求編號,可以利用逆數組或者哈希;
            <2>找到每一階段的合法狀態:即使是<1>中被判定為合法的狀態,在具體的各個階段中也未必合法(比如本題,如果某一行的某一個位置是'H',不能放,而某一個狀態在這里放了,則不合法),因此要對每個階段再枚舉一遍,找到真正合法的狀態,并計入一個vector;
            <3>找到狀態轉移中的合法狀態:在狀態轉移中,往往要求狀態不沖突(比如本題,在連續的三個階段中,都不能有某一位有兩個為1的情況),因此,還要枚舉每個狀態在轉移時與其不沖突的狀態,并計入vector。
            注意,有時候這一步不是很容易進行,需要在DP過程中進行;
            (2)DP過程中的精簡:
            DP過程中,枚舉狀態、轉移決策都只枚舉合法的,在vector里面調(注意vector里記錄的全都是狀態編號而不是表示!),可以大大減少枚舉量,不過有時候,還會有一些時間浪費,這時候,可以采取一些其它的辦法來精簡,比如再次進行DFS構造合法狀態等。

            總之,這類問題的目標就是“精簡,精簡,再精簡,使枚舉到的不合法狀態減到最少”。
            代碼:
            #include <iostream>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <string.h>
            #include 
            <vector>
            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 pb push_back
            #define IR iterator
            typedef vector 
            <int> vi;
            const int MAXN = 103, MAXM = 11, MAXS = 100, INF = ~0U >> 2;
            int n, m, S, A[MAXN], B[MAXS], T1[MAXS], F[MAXN][MAXS][MAXS], res;
            bool F0[MAXN][MAXS];
            vi P0[MAXN], P1[MAXS][MAXS];
            void init()
            {
                 scanf(
            "%d%d"&n, &m); getchar();
                 re1(i, n) {A[i] 
            = 0; re(j, m) A[i] |= ((getchar() == 'P'<< j); getchar();}
            }
            void dfs(int v, int ST)
            {
                
            if (v >= m) B[S++= ST; else {dfs(v + 3, ST | (1 << v)); dfs(v + 1, ST);}
            }
            void prepare()
            {
                S 
            = 0; dfs(00);
                re(i, S) {T1[i] 
            = 0for (int j=B[i]; j; j-=j&(-j)) T1[i]++;}
                re1(i, n) re(j, S) 
            if (!(~A[i] & B[j])) {P0[i].pb(j); F0[i][j] = 1;} P0[0].pb(S - 1); F0[0][S - 1= 1;
                re(i, S) re(j, S) 
            if (!(B[i] & B[j])) re(k, S) if (!(B[i] & B[k]) && !(B[j] & B[k])) P1[i][j].pb(k);
            }
            void solve()
            {
                re3(i, 
            0, n) re(j1, S) re(j2, S) F[i][j1][j2] = -INF; F[0][S - 1][S - 1= 0;
                vi::IR vi_e0, vi_e1, vi_e2; 
            int j0, j1, k, V;
                re(i, n) {
                    vi_e0 
            = P0[i].end(); if (i) vi_e1 = P0[i - 1].end(); else vi_e1 = P0[i].end();
                    
            for (vi::IR p=P0[i].begin(); p != vi_e0; p++)
                        
            for (vi::IR p_=P0[i ? i - 1 : i].begin(); p_ != vi_e1; p_++) {
                            j0 
            = *p; j1 = *p_;
                            
            if (!(B[j0] & B[j1])) {
                                vi_e2 
            = P1[j0][j1].end();
                                
            for (vi::IR p__ = P1[j0][j1].begin(); p__ != vi_e2; p__++) {
                                    k 
            = *p__;
                                    
            if (F0[i + 1][k]) {
                                        V 
            = F[i][j0][j1] + T1[k];
                                        
            if (V > F[i + 1][k][j0]) F[i + 1][k][j0] = V;
                                    }
                                }
                            }
                        }
                }
                res 
            = 0; re(i, S) re(j, S) if (F[n][i][j] > res) res = F[n][i][j];
            }
            void pri()
            {
                 printf(
            "%d\n", res);
            }
            int main()
            {
                init();
                prepare();
                solve();
                pri();
                
            return 0;
            }

            posted @ 2012-03-10 23:27 Mato_No1 閱讀(841) | 評論 (0)編輯 收藏

            【背景】
            2012年1月19日,本沙茶開始看動態樹論文,搞懂了一些;
            2012年1月20日,開始寫動態樹,使用的題是QTREE,花了整整一天時間總算寫完,交上去,TLE……
            2012年1月21日,又調了一天,對拍了100+組隨機數據都瞬間出解,交上去,還是TLE……
            2012年2月8日,WC2012,fanhq666講動態樹,又搞懂了一點,于是當天晚上回房間以后就開始繼續調,交上去,TLE……
            2012年2月9日,晚上繼續調QTREE,TLE……
            在挑戰動態樹N次失敗之后,本沙茶昨天再次去挑戰動態樹……這次換了一個題:BZOJ2002(傳說中的動態樹模板題)
            一開始還是TLE了,不過后來把數據搞到手以后,發現TLE的原因并不是常數大,而是死循環了,最后,經過2h+的調試,總算找到了錯誤(有好幾處),終于AC了……

            【關于BZOJ2002】
            從每個點i往(i+Ki)連一條邊,如果(i+Ki)不存在則往一個附加的結點(本沙茶的代碼中為1號點,因為0號點是不能使用的)連一條邊,這樣就是一棵樹(除1號點外,每個點有且只有一個后繼……),然后,問題中的兩種操作就是“改接”和“詢問到根的距離”,可以用動態樹搞;

            【代碼】
            #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--)
            const int MAXN = 200004, INF = ~0U >> 2;
            struct node {
                
            int c[2], p, sz;
                
            bool rf, d;
            } T[MAXN];
            int n;
            void sc(int _p, int _c, bool _d)
            {
                T[_p].c[_d] 
            = _c; T[_c].p = _p; T[_c].d = _d;
            }
            void upd(int No)
            {
                T[No].sz 
            = T[T[No].c[0]].sz + T[T[No].c[1]].sz + 1;
            }
            void rot(int No)
            {
                
            int p = T[No].p; bool d = T[No].d;
                
            if (T[p].rf) {T[p].rf = 0; T[No].rf = 1; T[No].p = T[p].p;} else sc(T[p].p, No, T[p].d);
                sc(p, T[No].c[
            !d], d); sc(No, p, !d); upd(p);
            }
            void splay(int No)
            {
                
            int p; while (!T[No].rf) if (T[p = T[No].p].rf) rot(No); else if (T[No].d == T[p].d) {rot(p); rot(No);} else {rot(No); rot(No);} upd(No);
            }
            int access(int x)
            {
                
            int tmp = 0;
                
            do {
                    splay(x); T[T[x].c[
            1]].rf = 1; T[tmp].rf = 0; sc(x, tmp, 1); upd(x); tmp = x; x = T[x].p;
                } 
            while (x);
            }
            void cut(int x)
            {
                access(x); splay(x); T[T[x].c[
            0]].rf = 1; T[T[x].c[0]].p = 0; sc(x, 00); upd(x);
            }
            void join(int x, int p)
            {
                access(x); T[x].p 
            = p;
            }
            int main()
            {
                
            int m, x, y, z;
                scanf(
            "%d"&n); n++;
                re3(i, 
            2, n) {scanf("%d"&x); T[i].sz = 1; T[i].rf = 1if (i + x <= n) T[i].p = i + x; else T[i].p = 1;}
                T[
            1].sz = 1; T[1].rf = 1; T[0].sz = 0;
                scanf(
            "%d"&m);
                re(i, m) {
                    scanf(
            "%d"&x);
                    
            if (x == 1) {
                        scanf(
            "%d"&y); y += 2;
                        access(y); splay(y); printf(
            "%d\n", T[T[y].c[0]].sz);
                    } 
            else {
                        scanf(
            "%d%d"&y, &z); y += 2;
                        cut(y); 
            if (y + z <= n) join(y, y + z); else join(y, 1);
                    }
                }
                
            return 0;
            }

            【易疵點】
            (1)注意一個點的父結點p有兩種可能:如果該結點是某棵伸展樹的根結點則p為它通過輕邊連向的另一棵伸展樹中的某一個點的編號(在原樹中,就是該結點所在伸展樹代表的鏈的最上層的那個節點的父結點),否則為該結點在伸展樹中的父結點編號(通過重邊相連);
            (2)在改接時刪除邊的時候,如果刪除的是輕邊則直接把父結點設為0即可,如果是重邊則要sc一下再將父結點設為0;
            (3)rot里面有一個地方很關鍵,極易疵!就是如果p是伸展樹的根結點,則除了No的rf改為1,p的rf改為0之外,還要把No的父結點設為p的父結點;
            (4)本題中不涉及整棵大樹的根(就是root),如果需要root,則rot中還要加一句:if (root == p) root = No;
            (5)cut里面有一種簡便寫法(不需要找到x的父結點的):先access(x),將x伸展到根之后,x及其右子樹就是原樹中以x為根的子樹,左子樹就是其它部分,所以直接將x與其左子結點斷開即可(注意斷開的是重邊所以要sc一下,再將x的左子結點的父結點設為0、rf設為1,再upd一下);
            (6)一定要搞清楚rf的變化(該改時一定要改!)

            最后,放上fanhq666超級大神的總結:
            01

            posted @ 2012-02-26 13:03 Mato_No1 閱讀(2063) | 評論 (2)編輯 收藏

            所謂數據結構詢問類題,就是那種一大堆操作+詢問的題(典型的有NOI2005 sequence、NOI2007 necklace等)。
            對于這種題常用數據結構有:線段樹(樹狀數組可以看成線段樹的簡化版本)、Segplaytree、各種塊狀結構,以及線段樹在樹結構上的應用——樹鏈剖分、Segplaytree在樹結構上的應用——Link-cut Tree實現動態樹;

            解題技巧:
            (1)看到題以后,首先搞清楚題目是基于什么結構的——線性結構、樹形結構、甚至可能還有圖結構(比如SCOI2011的那題,對于圖結構一般不能直接處理,而是采用三種辦法搞定:一是有向圖縮環轉化為有向樹、二是求生成樹、三是遍歷)
            (2)在基本的數據結構(上面列出來的那些)當中看看有木有可以支持所有的操作的(其實,如果遇到一種基本數據結構就能支持題目中所有的操作,那這題就太水了,一般像AH這樣的弱省全場30人以上AC木有問題,因此要特別小心),如果有就直接用這種數據結構搞了;
            (3)如果木有,就要想到數據結構的聯合或者是模型轉化;
            (4)然后就是寫代碼了,寫的時候要注意這種數據結構模板中的易疵點;
            (5)調試的時候,先檢查樣例和不超過5組的小數據,緊接著讀一遍代碼,觀察那些易疵點,然后立刻開始對拍,節省時間;
            (6)對拍的程序是很容易寫的;
            (7)造數據的時候可以造只有一種操作的,專門檢查這種操作有木有問題;
            (8)一定要設法考慮到并檢查各種特殊情況。

            posted @ 2012-01-22 23:08 Mato_No1 閱讀(287) | 評論 (0)編輯 收藏

            例題
            本沙茶覺得塊狀數組又好寫又有用(其實就是另一種樸素)……只是那個O(sqrt(n))的復雜度比較大而已(其實如果加上常數的話它并不比Segplaytree慢多少)
            編程技巧:
            (1)每塊長度設為m=floor(sqrt(n)),最后不足長度的不補值,設n0為總塊數(顯然n0=(n-1)/m+1);
            (2)設立LEN[i]=第i塊的實際長度(顯然除了最后一塊都是m),可以在建立塊狀數組(真正搞成塊狀,也就是二維)的時候得到;
            (3)對于區間[l, r],要注意:<1>l、r位于同一塊(l/m==r/m)的情況;<2>r位于最后一塊的情況;
            (4)別忘了同時更新原數組與塊狀數組;

            另外,本例題需要二分+找多少個比它小的這樣的操作,總時間復雜度是O(N*sqrt(N)*log2N*log2N)(幸虧N只有10000……)。

            如果有插入刪除元素,就需要用動態的塊狀鏈表了……極其難搞,本沙茶不敢試了……遇到這種題還是寫Segplaytree吧囧……

            #include <iostream>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <string.h>
            #include 
            <math.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--)
            const int MAXN = 100002, MAXM = 320, INF = ~0U >> 2;
            int n, m, n0, A[MAXN], T[MAXM][MAXM], LEN[MAXM], res;
            int cmp(const void *s1, const void *s2)
            {
                
            return *(int *)s1 - *(int *)s2;
            }
            void prepare()
            {
                m 
            = (int) floor(sqrt(n) + 1e-7); n0 = (n - 1/ m + 1; re(i, n0) LEN[i] = 0;
                re(i, n) T[i 
            / m][LEN[i / m]++= A[i];
                re(i, n0) qsort(T[i], LEN[i], 
            sizeof(int), cmp);
            }
            void opr0(int No, int x)
            {
                A[No] 
            = x; int S = No / m; re(i, LEN[S]) T[S][i] = A[S * m + i]; qsort(T[S], LEN[S], sizeof(int), cmp);
            }
            int opr1(int l, int r, int x)
            {
                
            int S0 = l / m, l0 = l % m, S1 = r / m, r0 = r % m, l1, r1, mid, res0 = 0;
                
            if (S0 == S1) re3(i, l0, r0) {if (A[S0 * m + i] < x) res0++;} else {
                    re2(i, l0, LEN[S0]) 
            if (A[S0 * m + i] < x) res0++;
                    re3(i, 
            0, r0) if (A[S1 * m + i] < x) res0++;
                    re2(i, S0
            +1, S1) {
                        l1 
            = 0; r1 = LEN[i];
                        
            while (l1 < r1) {
                            mid 
            = l1 + r1 >> 1;
                            
            if (T[i][mid] >= x) r1 = mid; else l1 = mid + 1;
                        }
                        res0 
            += l1;
                    }
                }
                
            return res0;
            }
            int main()
            {
                
            int M, a0, b0, x0, l, r, mid; char ss[20];
                scanf(
            "%d%d"&n, &M);
                re(i, n) scanf(
            "%d"&A[i]); prepare();
                re(i, M) {
                    scanf(
            "%s", ss);
                    
            if (ss[0== 'C') {
                        scanf(
            "%d%d"&a0, &x0); opr0(--a0, x0);
                    } 
            else {
                        scanf(
            "%d%d%d"&a0, &b0, &x0); a0--; b0--;
                        l 
            = 0; r = 1000000000;
                        
            while (l < r) {
                            mid 
            = l + r + 1 >> 1;
                            
            if (opr1(a0, b0, mid) < x0) l = mid; else r = mid - 1;
                        }
                        printf(
            "%d\n", l);
                    }
                }
                
            return 0;
            }



             

            posted @ 2012-01-17 20:31 Mato_No1 閱讀(1132) | 評論 (2)編輯 收藏

            【1】新型LCA算法:(在WJMZBMR神犇空間上發現的,系神犇自創,Orz!!!)
            這種算法可以在僅使用樹的路徑剖分預處理中求出的DEP和UP來求任意兩點的LCA,時間復雜度為O(log2N),不需要單獨的預處理。
            步驟(假設求a0、b0兩點的LCA):
            (1)若UP[a0]==UP[b0],則a0、b0位于同一條重鏈上,顯然a0、b0中深度小的那個就是LCA了,返回結果,結束;
            (2)若UP[a0]!=UP[b0]且DEP[UP[a0]]>=DEP[UP[b0]],則LCA不可能在a0所在的那條重鏈上。證明:若LCA在a0所在的重鏈上,則UP[a0]必然也是a0、b0的公共祖先,也就是UP[a0]是b0的祖先。由于UP[a0]的深度大于等于UP[b0],若DEP[UP[a0]]>DEP[b0],則UP[a0]顯然不可能是b0的祖先,否則,在b0所在的重鏈上必然存在一個點C,滿足DEP[C]=DEP[UP[a0]],顯然,C也是b0的祖先,這就說明在樹中同一深度處存在兩個不同的結點,它們都是b0的祖先,這是不可能的,所以,LCA不可能在a0所在重鏈上。那么,a0就可以上溯到UP[a0]的父結點處(也就是E[FA[UP[a0]]].a),b0不動,然后繼續判斷;
            (3)若UP[a0]!=UP[b0]且DEP[UP[a0]]<DEP[UP[b0]],則LCA不可能在b0所在的重鏈上,將b0上溯到E[FA[UP[b0]]].a,a0不動,繼續判斷。
            由于a0、b0最多上溯O(log2N)次,所以該算法一定能在O(log2N)時間內求出LCA(a0, b0)。
            該算法的應用很廣,不光可以在樹的路徑剖分中快速求出LCA,精簡代碼,同時也減少了一些時間(因為它不需要像RMQ那樣進行預處理),而且,在一般的求LCA問題中,也可以先剖分求出UP再求LCA。
            代碼:
            int LCA(int a, int b)
            {
                
            while (1) {
                    
            if (UP[a] == UP[b]) return DEP[a] <= DEP[b] ? a : b;
                    
            else if (DEP[UP[a]] >= DEP[UP[b]]) a = E[FA[UP[a]]].a; else b = E[FA[UP[b]]].a;
                }
            }

            【2】樹的路徑剖分模板總結:
            (1)預處理部分:由于采用新型LCA算法(注意,求LCA的過程寫成專門的函數),所以,原來預處理部分的后3步都不需要了,也就是只要前3步:第一步建有根樹求出FA、DEP;第二步求出SZ劃分輕重邊;第三步找重鏈建線段樹求出UP、ord、tot和root。那些為了求RMQ而設置的數組也不需要了。
            (2)操作部分:難點在于上溯過程和銜接。設待操作的路徑為a0->b0(注意是有向的,無向的也可以當成有向的處理),LCA0=LCA(a0, b0);
            對于點權型的樹,a0->LCA0的過程需要包含LCA0,而b0->LCA0的過程不能包含LCA0。因此當b0==LCA0時,第二步應該什么事都不做,所以要特判;此外,如果N==1(樹中只有一個結點),為了防止引用根的父結點,也需要直接特判掉,所以,上溯過程可以分以下4步:
            <1>特判:若n=1(此時必然有a0==b0==0),直接操作0號結點,結束;
            <2>(a0->LCA)若a0是父邊是輕邊的葉結點,則單獨處理a0,最新點設為a0,a0跳到a0的父結點處開始,否則從a0開始(上溯)。上溯終止條件為DEP[a0]<DEP[LCA0]或者上溯到根結點,每次處理時,設c=”UP[a0]不超越LCA?UP[a0]:LCA",對[c, a0]段處理(l0=ord[c], r0=ord[a0]),再將a0上溯到c的父結點處(若c是根結點則退出);處理時,線段樹中記錄的所有有向的(從左到右的)信息都要反向;銜接時應不斷向右銜接;
            <3>(b0->LCA)與<2>類似,兩個不同點:一是有向的信息不要反向,銜接時應不斷向左銜接;二是若c==LCA,則l0=ord[c]+1;
            <4>最后將<2>中和<3>中得到的兩個銜接鏈再銜接一下即可;

            對于邊權型的樹,a0->LCA0的過程和b0->LCA0的過程都要包含LCA0引出的邊,b0==LCA0以及N==1時不需要特判(因為它們會自動地什么事都不做);在處理過程中,l0=ord[c], r0=ord[a0]-1;要分輕邊和重鏈分別處理;每次a0上溯到c而不是c的父結點處;終止條件為DEP[a0]<=DEP[LCA0]。

            模板題:PKU2831(動態最小生成樹問題,需要涉及到最小生成樹中兩點之間路徑上的最大邊權,用樹的路徑剖分。其實本題有離線算法,不需要樹的路徑剖分)
            #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--)
            const int MAXN = 1001, MAXM = 100001, INF = ~0U >> 2;
            struct _edge {
                
            int a, b, w;
            } _E[MAXM], _E2[MAXM];
            struct edge {
                
            int a, b, w, pre, next;
                
            bool Z;
            } E0[MAXN 
            << 2], E[MAXN << 2];
            struct node {
                
            int maxw, lch, rch;
            } T[MAXN 
            << 2];
            int n, _m, m0, m, N, u[MAXN], Q[MAXN], FA[MAXN], DEP[MAXN], SZ[MAXN], UP[MAXN], ord[MAXN], w0[MAXN], tot[MAXN], root[MAXN], l0, r0, x0, res;
            bool vst[MAXN];
            void init_d()
            {
                re(i, n) E0[i].pre 
            = E[i].pre = E0[i].next = E[i].next = i;
                m0 
            = m = n;
            }
            void add_edge0(int a, int b, int w)
            {
                E0[m0].a 
            = a; E0[m0].b = b; E0[m0].w = w; E0[m0].pre = E0[a].pre; E0[m0].next = a; E0[a].pre = m0; E0[E0[m0].pre].next = m0++;
                E0[m0].a 
            = b; E0[m0].b = a; E0[m0].w = w; E0[m0].pre = E0[b].pre; E0[m0].next = b; E0[b].pre = m0; E0[E0[m0].pre].next = m0++;
            }
            void add_edge(int a, int b, int w)
            {
                E[m].a 
            = a; E[m].b = b; E[m].w = w; E[m].Z = 0; E[m].pre = E[a].pre; E[m].next = a; E[a].pre = m; E[E[m].pre].next = m++;
            }
            int cmp(const void *s1, const void *s2)
            {
                
            return ((_edge *)s1)->- ((_edge *)s2)->w;
            }
            int UFS_find(int x)
            {
                
            int r = x, tmp; while (u[r] >= 0) r = u[r]; while (u[x] >= 0) {tmp = u[x]; u[x] = r; x = tmp;} return r;
            }
            void UFS_union(int x1, int x2)
            {
                
            if (u[x1] >= u[x2]) {u[x2] += u[x1]; u[x1] = x2;} else {u[x1] += u[x2]; u[x2] = x1;}
            }
            int mkt(int l, int r)
            {
                
            int No = ++N;
                
            if (l == r) {T[No].maxw = w0[l]; T[No].lch = T[No].rch = 0;} else {
                    
            int mid = l + r >> 1, l_r = mkt(l, mid), r_r = mkt(mid + 1, r);
                    T[No].maxw 
            = T[T[No].lch = l_r].maxw >= T[T[No].rch = r_r].maxw ? T[l_r].maxw : T[r_r].maxw;
                }
                
            return No;
            }
            void prepare()
            {
                qsort(_E2, _m, 
            sizeof(_E2[0]), cmp);
                re(i, n) u[i] 
            = -1;
                
            int a, b, r1, r2, total = 0, maxsz, x, n0;
                re(i, _m) {
                    a 
            = _E2[i].a; b = _E2[i].b; r1 = UFS_find(a); r2 = UFS_find(b);
                    
            if (r1 != r2) {UFS_union(r1, r2); add_edge0(a, b, _E2[i].w); if (++total == n - 1break;}
                }
                re(i, n) vst[i] 
            = 0; Q[0= DEP[0= N = 0; vst[0= 1; FA[0= -1;
                
            for (int front=0, rear=0; front<=rear; front++) {
                    a 
            = Q[front];
                    
            for (int p=E0[a].next; p != a; p=E0[p].next) {
                        b 
            = E0[p].b;
                        
            if (!vst[b]) {FA[b] = m; DEP[b] = DEP[a] + 1; vst[b] = 1; Q[++rear] = b; add_edge(a, b, E0[p].w);}
                    }
                }
                rre(i, n) {
                    a 
            = Q[i]; SZ[a] = 1; maxsz = 0;
                    
            for (int p=E[a].next; p != a; p=E[p].next) {
                        b 
            = E[p].b; SZ[a] += SZ[b]; if (SZ[b] > maxsz) {maxsz = SZ[b]; x = p;}
                    }
                    
            if (SZ[a] > 1) E[x].Z = 1;
                }
                UP[
            0= ord[0= 0;
                re2(i, 
            1, n) {
                    a 
            = Q[i]; int p = FA[a]; if (E[p].Z) {UP[a] = UP[E[p].a]; ord[a] = ord[E[p].a] + 1;} else {UP[a] = a; ord[a] = 0;}
                    
            if (SZ[a] == 1 && E[FA[a]].Z) {
                        b 
            = UP[a]; n0 = ord[a]; for (int j=a; j!=b; j=E[FA[j]].a) w0[--n0] = E[FA[j]].w;
                        tot[b] 
            = ord[a]; root[b] = mkt(0, ord[a] - 1);
                        
            for (int j=a; j!=b; j=E[FA[j]].a) {tot[j] = tot[b]; root[j] = root[b];}
                    }
                }
            }
            int LCA(int a, int b)
            {
                
            while (1) {
                    
            if (UP[a] == UP[b]) return DEP[a] <= DEP[b] ? a : b;
                    
            else if (DEP[UP[a]] >= DEP[UP[b]]) a = E[FA[UP[a]]].a; else b = E[FA[UP[b]]].a;
                }
            }
            void opr0(int l, int r, int No)
            {
                
            if (l >= l0 && r <= r0) {if (T[No].maxw > res) res = T[No].maxw;} else {
                    
            int mid = l + r >> 1;
                    
            if (mid >= l0) opr0(l, mid, T[No].lch);
                    
            if (mid < r0) opr0(mid + 1, r, T[No].rch);
                }
            }
            int main()
            {
                
            int P, s, a0, b0, w0, LCA0, c;
                scanf(
            "%d%d%d"&n, &_m, &P); init_d();
                re(i, _m) {
                    scanf(
            "%d%d%d"&a0, &b0, &w0);
                    _E[i].a 
            = _E2[i].a = --a0; _E[i].b = _E2[i].b = --b0; _E[i].w = _E2[i].w = w0;
                }
                prepare();
                re(i, P) {
                    scanf(
            "%d%d"&s, &w0); a0 = _E[--s].a; b0 = _E[s].b; LCA0 = LCA(a0, b0);
                    res 
            = -INF;
                    
            while (DEP[a0] > DEP[LCA0]) {
                        
            if (E[FA[a0]].Z) {
                            
            if (DEP[UP[a0]] >= DEP[LCA0]) c = UP[a0]; else c = LCA0;
                            l0 
            = ord[c]; r0 = ord[a0] - 1; opr0(0, tot[a0] - 1, root[a0]); a0 = c;
                        } 
            else {
                            
            if (E[FA[a0]].w > res) res = E[FA[a0]].w;
                            a0 
            = E[FA[a0]].a;
                        }
                    }
                    
            while (DEP[b0] > DEP[LCA0]) {
                        
            if (E[FA[b0]].Z) {
                            
            if (DEP[UP[b0]] >= DEP[LCA0]) c = UP[b0]; else c = LCA0;
                            l0 
            = ord[c]; r0 = ord[b0] - 1; opr0(0, tot[b0] - 1, root[b0]); b0 = c;
                        } 
            else {
                            
            if (E[FA[b0]].w > res) res = E[FA[b0]].w;
                            b0 
            = E[FA[b0]].a;
                        }
                    }
                    puts(res 
            >= w0 ? "Yes" : "No");
                }
                
            return 0;
            }

            好了,對于模板也就到此為止了,接下來該搞應用了。

            posted @ 2012-01-14 12:34 Mato_No1 閱讀(1133) | 評論 (1)編輯 收藏

            僅列出標題
            共12頁: First 2 3 4 5 6 7 8 9 10 Last 
            国产香蕉久久精品综合网| 久久综合色之久久综合| 999久久久无码国产精品| 国产日产久久高清欧美一区| 99久久夜色精品国产网站| 久久精品国产99国产精品| 久久综合色老色| 人妻精品久久无码区| 久久天堂电影网| 久久久久久免费视频| 韩国免费A级毛片久久| 久久国产福利免费| 久久久国产精华液| 中文字幕亚洲综合久久| 中文字幕无码久久精品青草| 久久久国产精品亚洲一区| 久久99国产精品99久久| 亚洲伊人久久成综合人影院| 久久精品天天中文字幕人妻 | 热99RE久久精品这里都是精品免费| 99久久夜色精品国产网站 | 亚洲精品无码久久久久去q| 国内精品久久国产大陆| 一本一道久久a久久精品综合 | 亚洲午夜久久久久久久久电影网| 99久久免费国产特黄| 尹人香蕉久久99天天拍| 久久九九全国免费| 日韩久久久久久中文人妻| 久久久久人妻一区精品| 久久国产精品成人片免费| 亚洲精品99久久久久中文字幕 | 亚洲精品综合久久| 亚洲国产精品久久久久| 亚洲精品乱码久久久久久按摩 | 久久精品国产99国产精品导航| 99久久www免费人成精品| 久久水蜜桃亚洲av无码精品麻豆 | 国产成人精品久久免费动漫| 国产精品久久久香蕉| 久久久久久久久久免免费精品|