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

                 摘要: 原題地址本題就是,有N個變量,其取值只可能有三種:1、2、3。現在已知它們之間的一些大小關系,求有多少對變量(I, J)滿足(I的值+J的值)一定>或=或<(A的值+B的值),A、B是指定的兩個變量,且I、J、A、B互不相同。首先,對于值相等的變量,直接建出無向圖,按連通塊縮點,再考慮大于/小于關系,如果I的值大于J則在I所在連通塊與J所在連通塊之間連一條有向邊(從I到J)…...  閱讀全文

            posted @ 2012-09-25 21:26 Mato_No1 閱讀(750) | 評論 (0)編輯 收藏

            RQNOJ187 巧置擋板
            ———————————————————————————————————————————————————
            【背景(神犇不要囧視,3x)】
            本沙茶最近開始猛攻搜索、近似、隨機等算法,目標是A掉AHOI2012的后3題(在哪跌倒就從哪爬起)。昨天晚上找到這題,發現是搜索,于是開始捉……結果被折騰了一晚上加一上午才A掉,不過,看在這題可以系統性的反映DFS優化的一般步驟,忍了……另外,這題是本沙茶在RQNOJ上通過的第400題……回想起通過第300題的時候已經是近三年半之前了……真頹廢啊……
            ———————————————————————————————————————————————————
            普通DFS(不加迭代)的優化方法主要有:
            (1)可行性剪枝,如果遇到已經無法產生可行解的情況就剪枝,有時候,可行性剪枝也可以指在搜索過程中對不合法情況的剔除;
            (2)最優性剪枝:如果遇到已經無法產生比目前得到的最優解還要優的解的情況就剪枝,這其中包括啟發式最優性剪枝(即分支限界),對目前解的進展情況作樂觀估計,如果估計值都不能超越目前的最優解,則剪枝;
            (3)通過改變搜索順序,盡早得出較優解,從而為后面的剪枝提供余地;
            (4)通過預處理等手段,提前得到啟發值或者較優解,為后面的剪枝提供余地;

            一般步驟:
            (1)預處理,當然是寫在最前面,在搜索前得到啟發值等東東;
            (2)搜索過程中,先寫最優性剪枝(包括已經到達搜索終點了,也應該判斷一下,提前排除不是最優解的情況);
            (3)再判定搜索終點,如果到了,也不要馬上更新解,而是要判定這個解是否符合要求,再更新;
            (4)若未到終點,再寫可行性剪枝;
            (5)最后寫搜索過程,包括需要改變搜索順序的情況。

            注意事項:數組的分層問題。
            這是一個很嚴重的問題,因為分層出錯很可能會導致一些很怪的錯誤出現,難以發現。在搜索題的調試技巧這一篇中,已經提到了這個問題,本題又涉及到了。
            一般來說,搜索過程中使用的數組(包括變量,可以視為零維數組)可以分為以下三類:
            (1)在搜索過程中,只會引用其值,不會修改的數組(比如預處理中得到的那些東東),相當于常量;
            (2)在搜索過程中,會改變其值,但在搜索完畢后能改回原值的數組;
            (3)在搜索過程中,會改變其值,且在搜索完畢后也改不回原值的數組。
            對于(1)(2)類數組,不需分層,但對于第(3)類數組一定要分層。這是因為這些數組在多層搜索中都需要修改,而且搜索完了也改不回來,如果不分層,則當后面的搜索完畢以后,要繼續搜索前面的,引用的將是后面的值,就會出錯。
            對于第(3)類數組,有的已經“自然分層”(每層搜索修改的元素都不一樣),比如本題代碼中的R[][]、C[][]。然而有的并沒有自然分層,比如本題的len、pos[]、tmp[]等,因此對于這些數組,需要再加一維,對于不同層使用該維不同的元素即可。

            下面是本題的算法:
            使用DFS搜索每個位置的擋板是否需要放。搜索時,先搜豎的再搜橫的,由于題目要求每個連通塊都必須是矩形,因此對于豎的,如果該位置的上方兩個相鄰位置的橫的沒有全放(包括只放了一個),則該位置的豎的放不放取決于其上一行對應位置的豎的有沒有放(第一行除外),如果兩個橫的都放了,則這里的豎的既可以放也可以不放(先搜不放的情況)。當然,一行的兩個1之間必須至少有一個豎的,這是可行性限制。在搜橫的的時候,按照該行所放的豎的,分成若干段,顯然每一段要么下面都用橫的封上,要么一個都不封上。其中,如果該段所在的連通塊里面有1,且下一行對應位置也有1,則必須封上;若該段所在連通塊里面沒有1,則不能封上(因為不能有一個連通塊里一個1都沒有),否則可以自由選擇封還是不封(當然也是先搜不封的)。這樣一直搜到最后一行的豎的后,還要判斷最后一行有沒有連通塊里沒1,若沒有,則為一個可行解。啟發式優化:設D[i]為第i行及以后至少要幾個擋板(若某行有K個1,則至少需要(K-1)個豎的;若某列有K個1,則至少需要(K-1)個橫的,累加起來即可,顯然這是樂觀估計),D[i]可以預處理出來,當做啟發值。

            代碼:
            #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 = 35, INF = ~0U >> 2;
            int n, m, D[MAXN], len[MAXN], pos[MAXN][MAXN], tmp[MAXN][MAXN], res = INF;
            bool A[MAXN][MAXN], C[MAXN][MAXN], R[MAXN][MAXN], S[MAXN][MAXN][MAXN][MAXN];
            void dfs0(int No, int v, int sum, bool FF);
            void init()
            {
                scanf(
            "%d%d"&n, &m); int x;
                re(i, n) re(j, m) {scanf(
            "%d"&x); A[i][j] = x;}
            }
            void prepare()
            {
                re(i, n) re(j, m) {
                    S[i][j][i][j] 
            = A[i][j];
                    re2(k, i
            +1, n) S[i][j][k][j] = A[k][j] || S[i][j][k - 1][j];
                    re2(k, j
            +1, m) S[i][j][i][k] = A[i][k] || S[i][j][i][k - 1];
                    re2(k, i
            +1, n) re2(k0, j+1, m) S[i][j][k][k0] = A[k][k0] || S[i][j][k - 1][k0] || S[i][j][k][k0 - 1];
                }
                
            int sum;
                re(i, n) {
                    D[i] 
            = 0;
                    re2(j, i, n) {sum 
            = 0; re(k, m) if (A[j][k]) sum++if (sum) D[i] += sum - 1;}
                    re(k, m) {sum 
            = 0; re2(j, i, n) if (A[j][k]) sum++if (sum) D[i] += sum - 1;}
                }
            }
            void dfs1(int No, int v, int sum)
            {
                
            if (sum + D[No + 1>= res) return;
                
            if (v == len[No] + 1) dfs0(No + 10, sum, 1); else if (tmp[No][v] != 1) dfs1(No, v + 1, sum); else {
                    
            int l, r, sum0 = sum;
                    
            if (v) l = pos[No][v - 1+ 1else l = 0;
                    
            if (v == len[No]) r = m - 1else r = pos[No][v];
                    re3(j, l, r) R[No][j] 
            = 0; dfs1(No, v + 1, sum);
                    re3(j, l, r) {R[No][j] 
            = 1; sum0++;} dfs1(No, v + 1, sum0);
                }
            }
            void dfs0(int No, int v, int sum, bool FF)
            {
                
            if (sum + D[No + 1>= res) return;
                
            bool FF0; if (A[No][v]) {if (!FF) returnelse FF0 = 0;} else FF0 = FF;
                
            if (v == m - 1) {
                    
            if (No == n - 1) {
                        len[No] 
            = 0; re(i, m-1if (C[No][i]) pos[No][len[No]++= i;
                        
            int l, r, x; bool FFF = 1;
                        re3(i, 
            0, len[No]) {
                            
            if (i) l = pos[No][i - 1+ 1else l = 0;
                            
            if (i == len[No]) r = m - 1else r = pos[No][i];
                            x 
            = 0; rre(j, No) if (R[j][l]) {x = j + 1break;}
                            
            if (!S[x][l][No][r]) {FFF = 0break;}
                        }
                        
            if (FFF) res = sum;
                        
            return;
                    }
                    len[No] 
            = 0; re(i, m-1if (C[No][i]) pos[No][len[No]++= i;
                    
            int l, r, x, sum0 = sum;
                    re3(i, 
            0, len[No]) {
                        
            if (i) l = pos[No][i - 1+ 1else l = 0;
                        
            if (i == len[No]) r = m - 1else r = pos[No][i];
                        x 
            = 0; rre(j, No) if (R[j][l]) {x = j + 1break;}
                        
            if (S[x][l][No][r] && S[No + 1][l][No + 1][r]) {
                            tmp[No][i] 
            = 2; re3(j, l, r) {sum0++; R[No][j] = 1;}
                        } 
            else if (S[x][l][No][r]) {
                            tmp[No][i] 
            = 1; re3(j, l, r) R[No][j] = 0;
                        } 
            else {
                            tmp[No][i] 
            = 0; re3(j, l, r) R[No][j] = 0;
                        }
                    }
                    dfs1(No, 
            0, sum0);
                } 
            else if (No && (!R[No - 1][v] || !R[No - 1][v + 1])) {
                    
            if (C[No - 1][v]) {C[No][v] = 1; dfs0(No, v + 1, sum + 11);} else {C[No][v] = 0; dfs0(No, v + 1, sum, FF0);}
                } 
            else {
                    C[No][v] 
            = 0; dfs0(No, v + 1, sum, FF0);
                    C[No][v] 
            = 1; dfs0(No, v + 1, sum + 11);
                }
            }
            void pri()
            {
                printf(
            "%d\n", res);
            }
            int main()
            {
                init();
                prepare();
                dfs0(
            0001);
                pri();
                
            return 0;
            }


            posted @ 2012-09-23 15:16 Mato_No1 閱讀(882) | 評論 (0)編輯 收藏

            [SCOI2007 kshort]
            求圖的s-t第K短簡單路問題,若有長度相同的,字典序小的優先。

            首先,由于是簡單路,所以A*是不能做的,因為有可能有兩條s-i(i為某個中間點)路P1和P2,P1比P2短,但由于P1到達的頂點與P2不同,導致最終沿P1到達t的路徑長度長于沿P2到達t的(甚至有可能沿P1根本到不了t)。然后,如果直接用DFS,由于要求的是第K優解,而不是最優解,所以不能使用最優性剪枝(包括分支限界),因此專門為最優性剪枝服務的“改變搜索順序”技巧也不能使用了,因此,能夠使用的只有可行性剪枝,而本題的數據范圍使得這種算法必然TLE。

            正解是一種由迭代加深思想擴展得到的“迭代變優”DFS。設置一個路徑長度上限Z,搜索s到t的所有簡單路,在搜索過程中,遇到長度大于Z的路徑就停止(剪枝),然后,若得到路徑不足K條,則增加Z的值,重新開始搜索,直到得到的路徑總數大于等于K條為止。這里可以進行啟發式的優化,設g[i]為點i到t的最短路長度,則搜索過程中,假設目前搜到點i,則(目前路徑長度+g[i])是對整條路徑最短長度的樂觀估計,如果這個值超過了Z,就可以剪枝,但在剪枝之前要記下這個超過了Z的啟發值,取其中最小的作為下一次迭代的Z值。那么對于字典序腫么辦?可以在搜索過程中,強制先搜編號小的結點,這樣得到的s-t路徑必然是字典序遞增的。另外只要求出第K條路徑,搜索就可以終止,因為容易證明,題目要求的第K短的路徑一定已經求出來了(只不過不一定是最后一條而已),找到即可。此外,在搜索過程中不要忘了可行性剪枝,就是如果沿目前搜到的路徑已經到不了t了,剪枝。

            “迭代變優”DFS,就是設置一個解的評價值的上限(最小值)或下限(最大值),在搜索過程中,如果實際評價值(或者啟發值,如果可以加啟發式優化的話)越過這個限制,則剪枝。在一次搜索后,如果沒有得到符合要求的解,就將該限制值設為本次搜索過程中越界“越”得最近的那個值,重新開始搜索,直到找到所需要的解或者發現無解(如果一次搜索中沒有發生越界,卻仍然沒有找到解)為止。其應用主要體現在兩個方面:(1)搜索樹過深甚至無限深,但所需求的那個解卻不深的情況;(2)求第K優解的情況。

            代碼:
            #include <iostream>
            #include 
            <stdio.h>
            #include 
            <stdlib.h>
            #include 
            <string.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--)
            const int MAXN = 51, MAXK = 201, MAXM = 3000, INF = ~0U >> 2;
            struct edge {
                
            int a, b, w, pre, next;
            } E[MAXM 
            + MAXN], E0[MAXM + MAXN];
            struct edge0 {
                
            int a, b, w;
                
            bool operator< (edge0 e0) const {return b < e0.b;}
            } _E[MAXM];
            int n, m, s, t, K, dist[MAXN], Q[MAXN + 1];
            int Z, Z0, vst0[MAXN], _FL, len0, X00[MAXN], No, len[MAXK], X0[MAXK][MAXN], sum0[MAXK], reslen, res[MAXN];
            bool vst[MAXN], res_ex = 0;
            void init_d()
            {
                re(i, n) E[i].pre 
            = E[i].next = E0[i].pre = E0[i].next = i; m = n;
            }
            void add_edge(int a, int b, int w)
            {
                E[m].a 
            = a; E[m].b = b; E[m].w = w; E[m].pre = E[a].pre; E[m].next = a; E[a].pre = m; E[E[m].pre].next = m;
                E0[m].a 
            = b; E0[m].b = a; E0[m].w = w; E0[m].pre = E0[b].pre; E0[m].next = b; E0[b].pre = m; E0[E0[m].pre].next = m++;
            }
            void init()
            {
                
            int m0; scanf("%d%d%d%d%d"&n, &m0, &K, &s, &t); init_d(); s--; t--;
                re(i, m0) {scanf(
            "%d%d%d"&_E[i].a, &_E[i].b, &_E[i].w); _E[i].a--; _E[i].b--;}
                sort(_E, _E 
            + m0);
                re(i, m0) add_edge(_E[i].a, _E[i].b, _E[i].w);
            }
            void prepare()
            {
                re(i, n) {vst[i] 
            = 0; dist[i] = INF;} vst[t] = 1; dist[t] = 0; Q[0= t;
                
            int x, y, d0, d1;
                
            for (int front=0, rear=0!(!front && rear==|| front==rear+1); front==? front=0 : front++) {
                    x 
            = Q[front]; d0 = dist[x];
                    
            for (int p=E0[x].next; p != x; p=E0[p].next) {
                        y 
            = E0[p].b; d1 = d0 + E0[p].w;
                        
            if (d1 < dist[y]) {
                            dist[y] 
            = d1;
                            
            if (!vst[y]) {vst[y] = 1; Q[rear==? rear=0 : ++rear] = y;}
                        }
                    }
                    vst[x] 
            = 0;
                }
            }
            void dfs(int x, int sum)
            {
                
            if (x == t) {
                    
            if (sum <= Z) {sum0[No] = sum; len[No] = len0; re(i, len0) X0[No][i] = X00[i]; No++if (No == K) res_ex = 1;}
                    
            else if (sum < Z0) Z0 = sum;
                    
            return;
                } 
            else {
                    
            int h0 = sum + dist[x];
                    
            if (h0 > Z) {if (h0 < Z0) Z0 = h0; return;}
                    vst0[x] 
            = ++_FL; Q[0= x; int _x, _y;
                    
            for (int front=0, rear=0; front<=rear; front++) {
                        _x 
            = Q[front];
                        
            for (int p=E[_x].next; p != _x; p=E[p].next) {
                            _y 
            = E[p].b;
                            
            if (!vst[_y] && vst0[_y] != _FL) {vst0[_y] = _FL; Q[++rear] = _y;}
                        }
                    }
                    
            if (vst0[t] != _FL) return;
                    
            for (int p=E[x].next; p != x; p=E[p].next) {
                        _y 
            = E[p].b;
                        
            if (!vst[_y]) {vst[_y] = 1; X00[len0++= _y; dfs(_y, sum + E[p].w); if (res_ex) returnelse {len0--; vst[_y] = 0;}}
                    }
                }
            }
            void solve()
            {
                Z 
            = dist[s]; int No0 = 0; _FL = 0;
                
            while (1) {
                    Z0 
            = INF; No = 0; re(i, n) {vst[i] = 0; vst0[i] = 0;}
                    vst[s] 
            = 1; len0 = 1; X00[0= s; dfs(s, 0);
                    
            if (res_ex) {
                        No0 
            = K - No0;
                        re(i, K) 
            if (sum0[i] == Z) {No0--if (!No0) {reslen = len[i]; re(j, len[i]) res[j] = X0[i][j];}}
                        
            break;
                    } 
            else if (Z0 == INF) breakelse {No0 = No; Z = Z0;}
                }
            }
            void pri()
            {
                
            if (res_ex) {
                    printf(
            "%d", res[0+ 1);
                    re2(i, 
            1, reslen) printf("-%d", res[i] + 1);
                    puts(
            "");
                } 
            else puts("No");
            }
            int main()
            {
                init();
                prepare();
                solve();
                pri();
                
            return 0;
            }


            posted @ 2012-09-23 14:28 Mato_No1 閱讀(1539) | 評論 (0)編輯 收藏

            原題地址
            典型的二次遞推/DP的題目。
            首先,題目中的“不便利值”指的是某個點到根的路徑上的木有被選定鏈覆蓋的邊的條數。

            第一問:設F[i][0..2]分別為當子樹i中結點i的狀態為不參與鏈(0)、作為某鏈端點(1)、作為某鏈中間點(2)時,子樹i中的結點到i的最小不便利值。為了得到F,需要設立G[j][k(0..2)]表示結點i的前j棵子樹中,有k棵的根結點與結點i接上的最小的最大不便利值。顯然,不和i接上的,狀態為0、1、2都行,但不便利值要加1,而和i接上的狀態只能是0或1,不加1。

            問題是第二問。第二問的難點在于,當i取得最小不便利值時,i的每個子結點并非都取到最小不便利值。舉個例子,結點i的最小不便利值為3,它的某個子結點j的最小不便利值為2,則當j與i接上時,子樹j的內部既可以取不便利值為2的解,也可以取不便利值為3的解。所以,為了解決第二問,需要求出結點i的最小不便利值為x的解的總數。萬幸的是,x的范圍并不是太大,可以證明,x不會超過log3N(下取整),也就是當N=100000時x最大為10。因此,最后仍然不會T掉。

            這題的一個啟示就是,在求類似于“最優解計數”的問題中,不要認為當后面的狀態取得最優解時,前面的狀態一定取得最優解。因此,不能只記錄某狀態取得最優解的個數,而要記錄該狀態取得每一個可行解時的個數。

            代碼:
            #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 = 100010, MAXW = 11, INF = ~0U >> 2;
            struct edge {
                
            int a, b, pre, next;
            } E0[MAXN 
            * 3], E[MAXN << 1];
            int n, m0, m, Q[MAXN], F[MAXN][3], G[MAXN][3], res1 = 0;
            ll MOD, FS[MAXN][MAXW][
            3], S[MAXN][MAXW][3], res2 = 0;
            bool vst[MAXN];
            void init_d()
            {
                re(i, n) E0[i].pre 
            = E0[i].next = E[i].pre = E[i].next = i; m0 = m = n;
            }
            void add_edge0(int a, int b)
            {
                E0[m0].a 
            = a; E0[m0].b = b; 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].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)
            {
                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()
            {
                
            int _M; scanf("%d%d"&n, &_M); cin >> MOD; if (_M < n - 1) {res1 = res2 = -1return;} init_d(); int a0, b0;
                re2(i, 
            1, n) {scanf("%d%d"&a0, &b0); add_edge0(--a0, --b0);}
            }
            void prepare()
            {
                re(i, n) vst[i] 
            = 0; Q[0= 0; vst[0= 1int x, y;
                
            for (int front=0, rear=0; front<=rear; front++) {
                    x 
            = Q[front];
                    
            for (int p=E0[x].next; p != x; p=E0[p].next) {
                        y 
            = E0[p].b;
                        
            if (!vst[y]) {vst[y] = 1; Q[++rear] = y; add_edge(x, y);}
                    }
                }
                re(i, n) 
            if (!vst[i]) {res1 = -1; res2 = -1return;}
            }
            inline 
            int minv3(int s1, int s2, int s3)
            {
                
            int s0 = s1 <= s2 ? s1 : s2;
                
            return s0 <= s3 ? s0 : s3;
            }
            inline 
            int minv2(int s1, int s2)
            {
                
            return s1 <= s2 ? s1 : s2;
            }
            void solve()
            {
                
            int x, y, len, v1, v2, v01, v02; ll sum;
                rre(i, n) {
                    x 
            = Q[i]; len = 0; G[0][0= 0; G[0][1= G[0][2= INF;
                    
            for (int p=E[x].next; p != x; p=E[p].next) {
                        y 
            = E[p].b; len++;
                        v1 
            = minv3(F[y][0], F[y][1], F[y][2]) + 1; v2 = minv2(F[y][0], F[y][1]);
                        G[len][
            0= v1 >= G[len - 1][0? v1 : G[len - 1][0];
                        v01 
            = v1 >= G[len - 1][1? v1 : G[len - 1][1];
                        v02 
            = v2 >= G[len - 1][0? v2 : G[len - 1][0];
                        G[len][
            1= minv2(v01, v02);
                        v01 
            = v1 >= G[len - 1][2? v1 : G[len - 1][2];
                        v02 
            = v2 >= G[len - 1][1? v2 : G[len - 1][1];
                        G[len][
            2= minv2(v01, v02);
                    }
                    re(j, 
            3) F[x][j] = G[len][j];
                    re(j, MAXW) {S[
            0][j][0= 1; S[0][j][1= S[0][j][2= 0;} len = 0;
                    
            for (int p=E[x].next; p != x; p=E[p].next) {
                        y 
            = E[p].b; len++;
                        re(j, MAXW) re(k, 
            3) {
                            S[len][j][k] 
            = 0;
                            
            if (j) {
                                sum 
            = 0; re(k0, 3) {sum += FS[y][j - 1][k0]; if (sum >= MOD) sum -= MOD;}
                                S[len][j][k] 
            = (sum * S[len - 1][j][k]) % MOD;
                            }
                            
            if (k) {
                                sum 
            = 0; re(k0, 2) {sum += FS[y][j][k0]; if (sum >= MOD) sum -= MOD;}
                                S[len][j][k] 
            = (S[len][j][k] + sum * S[len - 1][j][k - 1]) % MOD;
                            }
                        }
                    }
                    re(j, MAXW) re(k, 
            3) FS[x][j][k] = S[len][j][k];
                }
                res1 
            = minv3(F[0][0], F[0][1], F[0][2]);
                res2 
            = 0; re(i, 3if (F[0][i] == res1) res2 += FS[0][F[0][i]][i]; res2 %= MOD;
            }
            void pri()
            {
                cout 
            << res1 << endl << res2 << endl;
            }
            int main()
            {
                init();
                
            if (!res1) prepare();
                
            if (!res1) solve();
                pri();
                
            return 0;
            }


            posted @ 2012-09-22 16:21 Mato_No1 閱讀(824) | 評論 (0)編輯 收藏

            相關鏈接

            由此看來,省隊(準確來說是“省集訓隊”)的壓力減小很多了囧……因為即使省選題目再坑爹,也有NOIP的40%墊著……
            顯然,NOIP必須得高分,甩開別人,甩得越遠越好,這樣才能在省集訓隊的選拔中占據優勢……
            不過,進了省集訓隊之后又腫么選,就不知道了……

            而且估計明年的省選應該木有這么坑爹了囧……
            其實……最近發現即使是AHOI2012的后3題,只要會各種搜索+近似也是可以搞到很多分的……至少加起來100分木問題(總分200就A隊了)……

            所以,實力終究是硬道理!!!!!!!!!!!!!!!!!!!!!!!!!!!!

            我要進省隊!!!!
            我要進國家集訓隊!!!!

            posted @ 2012-09-21 21:34 Mato_No1 閱讀(577) | 評論 (0)編輯 收藏

            最近做了兩道LIS模型題,感覺到模型比較好,總結一下囧。
            【1】[HAOI2007]上升序列
            預處理:設F[i]為以i開頭的最長上升序列的長度,怎么求不用說了吧囧……
            假設目前需要求長度為M的、標號字典序最小的上升序列,顯然其第一個元素A[i]必須滿足F[i]>=M(注意,不是等于,是大于等于!),找到滿足這個條件的最小的i即可。然后,設目前已經求出了該序列的第x個元素為A[y],則第(x+1)個元素A[z]需要滿足的條件是A[z]>A[y],且F[z]=F[y]-1,找到滿足這個條件的最小的z即為該序列的第(x+1)個元素。按照這種方法,掃描一遍就可以求出整個序列,時間復雜度為O(N)。如果整個序列的最長上升序列長度<M,則無解。

            代碼:
            #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 = 10010,    MAXM = 1010, INF = ~0U >> 2;
            int n, m, len, A[MAXN], F[MAXN], D[MAXN], res[MAXM];
            void prepare()
            {
                D[len 
            = 0= INF; int l, r, mid;
                rre(i, n) 
            if (A[i] < D[len]) D[F[i] = ++len] = A[i]; else {
                    l 
            = 0; r = len;
                    
            while (l < r) {
                        mid 
            = l + r + 1 >> 1;
                        
            if (A[i] < D[mid]) l = mid; else r = mid - 1;
                    }
                    F[i] 
            = l + 1; D[l + 1= A[i];
                }
            }
            void solve()
            {
                
            int x, y;
                re(i, n) 
            if (F[i] >= m) {
                    res[
            0= A[i]; if (m == 1return; x = m - 1; y = 1;
                    re2(j, i
            +1, n) if (F[j] >= x && A[j] > res[y - 1]) {res[y++= A[j]; if (y == m) returnelse x--;}
                }
            }
            int main()
            {
                scanf(
            "%d"&n); re(i, n) scanf("%d"&A[i]);
                prepare();
                
            int m_s; scanf("%d"&m_s);
                re(i, m_s) {scanf(
            "%d"&m); if (m > len) puts("Impossible"); else {solve(); re(j, m-1) printf("%d ", res[j]); printf("%d\n", res[m - 1]);}}
                
            return 0;
            }


            【2】[HAOI2006]數字序列
            首先,由于序列的所有元素都是整數,所以可以將原序列的所有元素減去它的下標,這樣就把上升序列轉化為不下降序列了。
            第一問的結果顯然就是(N-新序列的最長不下降序列長度)。關鍵在于第二問。以下A均表示新序列。
            設F[i]為以A[i]結尾的最長不下降序列長度(同樣,求法不用說了),G[i]為在A[i]不修改的前提下將A[0..i]轉變為不下降序列的最小修改量。首先求出F[i],然后在求G[i]時,枚舉上一個“不動點”(就是不修改的元素)A[j](顯然必須滿足A[j]<=A[i]且F[j]=F[i]-1),這樣最小修改量就是G[j]+(將A[j..i]轉變為不下降序列的最小修改量)。可以證明,A[j..i]的最優修改方案必然是將A[j+1..t]全部修改為A[j],A[t+1..i]全部修改為A[i],這里t是一個[j..i]范圍的值。問題就是如何求出最優的t?
            一開始,假設t=j,即把A[j+1..i-1]全部修改為A[i],計算出修改量,設為S。然后,由于A[j+1..i-1]之間的元素要么小于A[j],要么大于A[i](這個是顯然的囧),我們把小于A[j]的元素稱為“小數”,把大于A[i]的元素稱為“大數”,則當t取t0時,修改量為S-(A[i]-A[j])*(A[j+1..t0]中的“小數”個數減去“大數”個數)。這樣,只需掃描一下,求出使得(A[j+1..t0]中的“小數”個數減去“大數”個數)值最大的t0即可。
            當然還有一個問題,對于同一個i,滿足“A[j]<=A[i]且F[j]=F[i]-1”的元素個數可能有很多,如果一個一個枚舉,一個一個掃描,會很慢的囧……解決方法是,求出滿足這個條件的j中最小的一個,設為j0,然后把A[j0+1..i-1]中的所有“小數”和“大數”全部處理出來,然后用類似前綴和的方法就能搞了囧……當然,為了找到j0,需要建一個二分圖,邊為(F[i], i)。
            最后,為了方便,可以把A序列的左邊加一個-INF,右邊加一個+INF。最后總的時間復雜度,理論上為O(N2),但由于是隨機數據,所以遠遠達不到這個級別。

            代碼:
            #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 = 40010, INF = ~0U >> 2;
            struct edge {
                
            int a, b, pre, next;
            } E[MAXN 
            << 1];
            int n, m, A[MAXN], D[MAXN], F[MAXN], W[MAXN], res1;
            ll G[MAXN], res2;
            void init_d()
            {
                re(i, n) E[i].pre 
            = E[i].next = i; m = n;
            }
            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);
                A[
            0= -INF; re1(i, n) {scanf("%d"&A[i]); A[i] -= i;} A[++n] = INF; n++;
            }
            void solve()
            {
                init_d(); F[
            0= 0; G[0= 0; D[0= -INF; add_edge(00); int len = 0, l, r, mid, x, maxw; ll sum, tmp;
                re2(i, 
            1, n) {
                    
            if (A[i] >= D[len]) D[F[i] = ++len] = A[i]; else {
                        l 
            = 0; r = len;
                        
            while (l < r) {
                            mid 
            = l + r + 1 >> 1;
                            
            if (A[i] >= D[mid]) l = mid; else r = mid - 1;
                        }
                        D[F[i] 
            = ++l] = A[i];
                    }
                    
            for (int p=E[F[i]-1].next; ; p=E[p].next) if (A[i] >= A[x = E[p].b]) break;
                    W[x] 
            = 0; re2(j, x+1, i) if (A[j] < A[i]) W[j] = W[j - 1+ 1else W[j] = W[j - 1- 1;
                    sum 
            = 0; maxw = -INF; G[i] = ~0Ull >> 2;
                    rre2(j, i, x) {
                        
            if (A[j] <= A[i] && F[j] == F[i] - 1) {
                            tmp 
            = G[j] + sum; if (tmp < G[i]) G[i] = tmp;
                            tmp 
            = G[j] + sum - (ll) (maxw - W[j]) * (A[i] - A[j]); if (tmp < G[i]) G[i] = tmp;
                        }
                        
            if (A[j] > A[i]) sum += A[j] - A[i]; else sum += A[i] - A[j];
                        
            if (W[j] > maxw) maxw = W[j];
                    }
                    add_edge(F[i], i);
                }
                res1 
            = n - F[n - 1- 1; res2 = G[n - 1];
            }
            void pri()
            {
                cout 
            << res1 << endl << res2 << endl;
            }
            int main()
            {
                init();
                solve();
                pri();
                
            return 0;
            }


            posted @ 2012-09-08 20:40 Mato_No1 閱讀(1454) | 評論 (2)編輯 收藏

                 摘要: 原題地址這個算法是由本沙茶在現場使用的那個做法擴展得來的……其實AC不了,后兩個點會因為常數過大而T掉……但在BZOJ上算總時間的話能AC……首先考慮樹的情形。設F[i]為從點i開始,往子樹i里面走,到達葉結點的期望長度,則很容易得到遞推公式:F[i] = (ΣF[j] + W(i, j)) / K,其中j是i的子結...  閱讀全文

            posted @ 2012-09-08 19:27 Mato_No1 閱讀(424) | 評論 (0)編輯 收藏

            原題地址
            本沙茶的第一個無向環套樹模型,紀念一下……

            環套樹,指的是每個連通塊中點數都等于邊數的無向圖(稱為無向環套樹)或者是每個點有且只有一個前趨(稱為內向環套樹)或后繼(稱為外向環套樹)的有向圖,由于這個圖的每個連通塊當中有且只有一個環(注意,可能是自環,即長度為1的環),且這個環上的每個點都可以當作根引出一棵樹,所以叫“環套樹”。

            對于無向環套樹,先將整個圖進行一次DFS,當中如果發現有逆向邊(這條邊第一次被發現必然是作為逆向邊的,也就是起點是終點的后代),就說明找到了這個環,記錄其起點和終點(注意,如果有多個連通塊的話,不能退出,要繼續遍歷完),再不斷上溯(因此在DFS過程中當然要記錄父邊了囧),就可以找到整個環了,然后再以環上的結點為根建樹即可,這樣依次處理每個連通塊。

            對于內向環套樹(外向類似),找環更為簡單,只需要任選一個點,不斷去找它的前趨,同時記錄找到的點序列,直到某個點在序列中出現兩次為止,此時這個點以及序列中它兩次出現的位置中間的所有點,就是環上的點,順序也順便記錄下來,然后樹也不用建了,直接在原圖中找就行了。

            對于這題,由于每個點都有且只有一個后繼,所以是外向環套樹,不過本沙茶更傾向于它的基圖(無向圖,是無向環套樹),然后就是一個DP了囧……

            代碼:
            #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 = 1000010;
            const ll INF = ~0Ull >> 2;
            struct edge {
                
            int a, b, pre, next;
            } E0[MAXN 
            * 3], E[MAXN << 1];
            int n, m0, m, A[MAXN], stk[MAXN], st[MAXN], pr[MAXN], Q[MAXN];
            ll F[MAXN][
            2], res = 0;
            bool vst[MAXN], vst0[MAXN];
            void init_d()
            {
                re(i, n) E0[i].pre 
            = E0[i].next = E[i].pre = E[i].next = i; if (n & 1) m0 = n + 1else m0 = n; m = n;
            }
            void add_edge0(int a, int b)
            {
                E0[m0].a 
            = a; E0[m0].b = b; 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].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)
            {
                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); int x; init_d();
                re(i, n) {
                    scanf(
            "%d%d"&A[i], &x); add_edge0(--x, i);
                }
            }
            void sol0(int x)
            {
                Q[
            0= x; int i, j, front, rear;
                
            for (front=0, rear=0; front<=rear; front++) {
                    i 
            = Q[front];
                    
            for (int p=E0[i].next; p != i; p=E0[p].next) {
                        j 
            = E0[p].b;
                        
            if (!vst0[j]) {Q[++rear] = j; vst0[j] = 1; add_edge(i, j);}
                    }
                }
                rre3(z, rear, 
            0) {
                    i 
            = Q[z];
                    F[i][
            0= 0; F[i][1= A[i];
                    
            for (int p=E[i].next; p != i; p=E[p].next) {
                        j 
            = E[p].b; F[i][0+= F[j][0>= F[j][1? F[j][0] : F[j][1]; F[i][1+= F[j][0];
                    }
                }
            }
            void solve()
            {
                re(i, n) {vst[i] 
            = vst0[i] = 0; st[i] = E0[i].next;} int x, y, tp, x0, y0; bool FF, FF2; ll tmp0, tmp1, tmp00, tmp01, res0;
                re(i, n) 
            if (!vst[i]) {
                    stk[tp 
            = 0= i; vst[i] = 1; FF2 = 0;
                    
            while (tp >= 0) {
                        x 
            = stk[tp]; FF = 0;
                        
            for (int p=st[x]; p != x; p=E0[p].next) {
                            y 
            = E0[p].b;
                            
            if (!vst[y]) {vst[y] = 1; stk[++tp] = y; pr[y] = p; st[x] = E0[p].next; FF = 1break;}
                            
            else if (p != (pr[x] ^ 1&& !FF2) {FF2 = 1; x0 = x; y0 = y;}
                        }
                        
            if (!FF) tp--;
                    }
                    
            if (FF2) {
                        tp 
            = 0; vst0[y0] = 1while (x0 != y0) {stk[tp++= x0; vst0[x0] = 1; x0 = E0[pr[x0]].a;} stk[tp++= y0;
                        re(j, tp) sol0(stk[j]);
                        tmp0 
            = F[stk[0]][0]; tmp1 = -INF;
                        re2(j, 
            1, tp) {
                            tmp00 
            = (tmp0 >= tmp1 ? tmp0 : tmp1) + F[stk[j]][0];
                            tmp01 
            = tmp0 + F[stk[j]][1];
                            tmp0 
            = tmp00; tmp1 = tmp01;
                        }
                        res0 
            = tmp0 >= tmp1 ? tmp0 : tmp1;
                        tmp0 
            = -INF; tmp1 = F[stk[0]][1];
                        re2(j, 
            1, tp) {
                            tmp00 
            = (tmp0 >= tmp1 ? tmp0 : tmp1) + F[stk[j]][0];
                            tmp01 
            = tmp0 + F[stk[j]][1];
                            tmp0 
            = tmp00; tmp1 = tmp01;
                        }
                        res 
            += res0 >= tmp0 ? res0 : tmp0;
                    }
                }
            }
            void pri()
            {
                cout 
            << res << endl;
            }
            int main()
            {
                init();
                solve();
                pri();
                
            return 0;
            }


            posted @ 2012-09-01 17:03 Mato_No1 閱讀(1061) | 評論 (0)編輯 收藏

            最近參加了N多模擬賽……現在統一總結一下。
            那些有代表性的題目總結一下。

            (1)Aug.16 Poetize 杯NOIP模擬賽 I
            (竟然AK了,虐場虐得真爽)
            第一題:容易發現如果新加入的那條邊連接的是同一個連通塊,結果乘2加1,如果是不同的連通塊,結果不變。證明:如果新邊(i, j)的是同一個連通塊,則原來i到j必然有路徑,設P為i到j的一條路徑,則在加入新邊以前,原圖的所有滿足條件的子圖都可以對P異或后得到一個新的圖,該圖僅有i、j兩個的度為奇數,其余點的度均為偶數,加入(i, j)之后得到一個新的滿足條件的子圖,所以乘2,注意(i, j)加上P也是一個滿足條件的子圖,所以加1;如果原來i和j不在同一個連通塊中,那么必定不存在包含(i, j)的滿足條件的子圖(否則這個子圖將(i, j)刪掉后會得到兩個頂點度數和為奇數的連通塊,這是不可能的),所以不變;

            (2)Aug.17 Clover 杯NOIP模擬賽 I
            第一題:判斷一個點是否在三角形(其實對于所有的凸多邊形都是這樣)時,不需要引射線,只需把三角形的三個頂點按逆時針排序,然后判斷該點往三角形的三對相鄰頂點(按逆時針)的叉積是否都非負就行了;
            第三題:枚舉起點,然后狀態壓縮DP,注意最后一個點必須壓縮一下才能不MLE;

            (3)Aug.18 Vijos復活邀請賽
            第一題:比較WS的幾何題。判斷圓與邊平行于坐標軸的矩形是否相交的時候,可以采用以下的簡便方法:一個圓與一個邊平行于坐標軸的矩形相交,當且僅當矩形的四個頂點至少有一個在圓內,或者圓的四個橫縱坐標最值點(最上、最下、最左、最右的點)至少有一個在矩形內,或者圓心在矩形內。
            第三題:主要難在s-t兩條路徑怎么找出來的問題,方法:以s為根建有根樹,找到到t的路徑后,將那條不在樹中的邊的兩端點到根的路徑上的所有邊都標記上,然后,若這棵樹中s-t路徑上的所有邊都沒標記,則s-t只有一條路徑,否則任選一條s-t路徑上的標記邊刪掉,然后再以s為根建一次有根樹,就可以找到第二條s-t路徑了;

            (4)Aug.19 [Vani有約會]杯邀請賽 II
            第二題:本沙茶的80%做法有點另類,是狀態壓縮DP,因為對于N<=800的數據,只有2、3、5、7、11、13、17、19、23這9個質數可能出現兩次以上,其余的都只會出現一次,所以建立10個容器(別忘了1),分別分配1和這9個質數,再對剩下的質數狀態壓縮即可(一開始只建了9個容器,結果N=27掛了);

            (5)Aug.20 『Citric杯』NOIP提高組模擬賽 I
            第一題:這也太太太坑爹了吧囧……居然在精度上做手腳(Orz @sillycross),注意用long double就行了,不過正解是變除法為乘法;

            (6)Aug.21 squarefk NOIP模擬賽
            第三題:對于一個這樣的序列A[0..N-1]:0<=Ai<=Ui,設F(A)為(A的0元素的個數)^(A的所有非0元素的乘積),問題就是求所有A的F值之積。顯然,指數是不能取模的,所以要設F[i][j][k]為前i位j個非0,底數為k的值,遞推式還是很容易的囧;

            (7)Aug.22 Clover 杯NOIP模擬賽 II
            第二題:問的是無向圖中兩點間是否有且僅有一條路徑的問題。首先肯定是判是否在同一連通塊,然后,對每個連通塊任意建一棵生成樹,如果這兩點在生成樹上的路徑上的每條邊都是橋,顯然只有一條路徑(因為每條邊都必須經過),否則,必有其它的路徑(某條邊不是橋,也就是可以不經過),所以求橋之后就轉化為了一個經典模型了囧……注意求LCA用類似路徑剖分的算法比較好寫……
            第三題:很容易想到遞推,F[i][j]:用i個布條,最后一個顏色為j,總方案數(下面的不解釋了),問題是,如果至少有一種顏色能和兩個或兩個以上的顏色相配,還不會有事,因為結果一定不會超過log2(1018),但如果每種顏色都最多只能和一種顏色相配腫么辦?因此這個需要特判:首先那些不能和任何顏色相配的肯定只能長度為1,減掉,然后剩下的每種顏色開始的每個長度的旗幟都各有一個,除以顏色總數(上取整)即可。

            (8)Aug.23 Poetize 杯NOIP模擬賽 II 暨Tyvj龍年七夕歡樂賽
            (第一題正解是貪心,本沙茶用費用流亂搞,T了兩個點)
            第三題:A先mod B消去整數。首先考慮有限小數的情況。結果是有限小數時,設小數點后位數為M,則必有A*K^M mod B=0,顯然這個方程有解的充要條件是B的每個質因數也都得是A*K的質因數,只要把B的質因數當中減掉A的,再看看剩下的質因數K是不是都有,都有的話剩下的就是亂搞一下了囧……如果不都有說明是循環小數,設混循環部分位數為M,循環節位數為R,則有A*(K^(M+R)-K^M) mod B = 0,整理得A*K^M*(K^R-1) mod B = 0,注意K^M與(K^R-1)是互質的,也就是把B的質因數中減掉A的之后,剩下的每個質因數,要么就是K有,要么就是(K^R-1)有。這樣,可以用類似于有限小數的辦法來求出M,對于剩下的(K沒有的)質因數,設它們的積(包含指數)為W,則必有K^R mod W = 1,K和W互質,根據歐拉定理,phi(W)必然是一個解,但不一定是最小正整數解,不過,可以肯定的是,最小正整數解一定是phi(W)的因數(不一定是質因數!!),因為若最小正整數解R0不是phi(W)的因數,設phi(W)=P*R0+Q(0<Q<R0),因為K^(P*R0+Q) = (K^R0)^P * K^Q mod W = 1,而(K^R0)^P mod W顯然也是1,所以必有K^Q mod W=1,這樣就得到了一個比R0還小的正整數解Q,這與R0是最小正整數解矛盾。因此,枚舉phi(W)的因數,再用快速冪判斷即可。

            (9)Aug.25 『Citric杯』NOIP提高組模擬賽 II
            第一題:這也太太太太太太太太太太太太太太太太坑爹了吧囧……其實題目描述有漏洞的,因為題目中并木有說表示結束的詢問一定是輸入的最后一個……
            第三題:這題本沙茶的做法巨另類也巨繁瑣(就當字符串算法的綜合復習了囧……),首先一頭一尾兩段的字符串還是很好找的……結尾的那段直接枚舉長度,開頭的的那段可以在整個字符串左邊用一個類似KMP的辦法搞(其實只要知道KMP的原理就能用它解決N多奇特問題了囧……),難點在于中間的那段字符串,需要是一個長度為奇數的回文串。為了快速找出一段連續子串里最長的長度為奇數的回文串,可以設G[i]為以i為中心的最長回文串長度,這可以將整個字符串逆序一下后接在原串后面(注意中間要加一個未出現的字符),然后用后綴數組解決(經典模型)。注意在找最長回文串的時候不能直接求中間G[i]的最大值,因為可能延伸出去,正解是二分枚舉這個長度,然后在中間不會延伸出去的那一段里找G的最大值,看是否符合要求。總時間復雜度O(NlogN)。

            (10)Aug.26 RQNOJ2012年八月月賽
            第二題:比賽的時候本沙茶用單調隊列硬搞搞不出來,后來寫樸素了(悲劇)……正解是將所有的前綴和按照先值遞增,再下標遞減排序,并串成Dancing Link,然后從按下標從大到小依次刪掉每個前綴和,這樣,每個前綴和左邊的那個一定是比值比它小的前綴和中值最大(值相同則下標最小)的那個,剩下就不解釋了囧……

            (11)Aug.28 「Clover」杯NOIP模擬賽 III
            第三題:先把每條無向邊拆成兩條有向邊,然后對這個有向圖求源點為1的單源最短路徑圖(就是由所有滿足D[i] + W<i, j> = D[j]的邊<i, j>組成的圖),顯然從這個圖的點1開始無論怎么走都是最短路徑,而且這個圖顯然是無環的(因為原圖中的每條邊權值都是正數,說實話,如果不滿足這個條件,這題就巨難做了,至少本沙茶不會做了),題目中要求的樹就是在這個新圖中從點1開始的一棵外向樹,由于這個新圖無環,所以只需要將每個點(除了1)都找一個父結點就行了,結果就是除1外的所有點入度之積。

            posted @ 2012-08-31 18:05 Mato_No1 閱讀(703) | 評論 (1)編輯 收藏

            從今天開始,本沙茶的這個空間重新啟用。
            ———————————————————————————————————————————————————
            AHOI2012,恥辱的過去,
            AHOI2013,光明的未來,
            為了自己的集訓隊夢想,從今天開始,全力復仇!!!
            ———————————————————————————————————————————————————
            以后這個空間里的文章題目都會以【AHOI2013復仇】為前綴……

            posted @ 2012-08-26 09:14 Mato_No1 閱讀(481) | 評論 (2)編輯 收藏

            僅列出標題
            共12頁: 1 2 3 4 5 6 7 8 9 Last 
            99久久国产宗和精品1上映| 久久免费大片| 久久午夜无码鲁丝片| 精品久久久久久无码专区不卡| 国产日产久久高清欧美一区| 久久人人爽人人爽人人AV东京热| 久久婷婷国产综合精品 | WWW婷婷AV久久久影片| 国产亚洲欧美成人久久片| 久久久久97国产精华液好用吗| 最新久久免费视频| 亚洲一本综合久久| 久久受www免费人成_看片中文| 99久久免费国产特黄| 色综合久久中文字幕综合网| 国产精品毛片久久久久久久| 一本久久综合亚洲鲁鲁五月天亚洲欧美一区二区 | 国产高清国内精品福利99久久| 亚洲狠狠婷婷综合久久久久| 久久久久亚洲av成人无码电影| 精品熟女少妇a∨免费久久| 久久久久亚洲AV片无码下载蜜桃| 色成年激情久久综合| 久久精品亚洲日本波多野结衣 | 69国产成人综合久久精品| 久久久九九有精品国产| 18禁黄久久久AAA片| 麻豆久久| 久久人人爽人人爽AV片| 国产精品99久久久久久猫咪 | 香港aa三级久久三级| 国产∨亚洲V天堂无码久久久| 无码八A片人妻少妇久久| 久久av免费天堂小草播放| 精品人妻伦九区久久AAA片69| 7国产欧美日韩综合天堂中文久久久久| 久久天堂AV综合合色蜜桃网| 久久精品www人人爽人人| 2021少妇久久久久久久久久| 国产精品久久久久久搜索| 久久国产精品国产自线拍免费|