• <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個(gè)變量,其取值只可能有三種:1、2、3。現(xiàn)在已知它們之間的一些大小關(guān)系,求有多少對(duì)變量(I, J)滿足(I的值+J的值)一定>或=或<(A的值+B的值),A、B是指定的兩個(gè)變量,且I、J、A、B互不相同。首先,對(duì)于值相等的變量,直接建出無(wú)向圖,按連通塊縮點(diǎn),再考慮大于/小于關(guān)系,如果I的值大于J則在I所在連通塊與J所在連通塊之間連一條有向邊(從I到J)…...  閱讀全文

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

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

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

            注意事項(xiàng):數(shù)組的分層問(wèn)題。
            這是一個(gè)很?chē)?yán)重的問(wèn)題,因?yàn)榉謱映鲥e(cuò)很可能會(huì)導(dǎo)致一些很怪的錯(cuò)誤出現(xiàn),難以發(fā)現(xiàn)。在搜索題的調(diào)試技巧這一篇中,已經(jīng)提到了這個(gè)問(wèn)題,本題又涉及到了。
            一般來(lái)說(shuō),搜索過(guò)程中使用的數(shù)組(包括變量,可以視為零維數(shù)組)可以分為以下三類:
            (1)在搜索過(guò)程中,只會(huì)引用其值,不會(huì)修改的數(shù)組(比如預(yù)處理中得到的那些東東),相當(dāng)于常量;
            (2)在搜索過(guò)程中,會(huì)改變其值,但在搜索完畢后能改回原值的數(shù)組;
            (3)在搜索過(guò)程中,會(huì)改變其值,且在搜索完畢后也改不回原值的數(shù)組。
            對(duì)于(1)(2)類數(shù)組,不需分層,但對(duì)于第(3)類數(shù)組一定要分層。這是因?yàn)檫@些數(shù)組在多層搜索中都需要修改,而且搜索完了也改不回來(lái),如果不分層,則當(dāng)后面的搜索完畢以后,要繼續(xù)搜索前面的,引用的將是后面的值,就會(huì)出錯(cuò)。
            對(duì)于第(3)類數(shù)組,有的已經(jīng)“自然分層”(每層搜索修改的元素都不一樣),比如本題代碼中的R[][]、C[][]。然而有的并沒(méi)有自然分層,比如本題的len、pos[]、tmp[]等,因此對(duì)于這些數(shù)組,需要再加一維,對(duì)于不同層使用該維不同的元素即可。

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

            代碼:
            #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 閱讀(878) | 評(píng)論 (0)編輯 收藏

            [SCOI2007 kshort]
            求圖的s-t第K短簡(jiǎn)單路問(wèn)題,若有長(zhǎng)度相同的,字典序小的優(yōu)先。

            首先,由于是簡(jiǎn)單路,所以A*是不能做的,因?yàn)橛锌赡苡袃蓷ls-i(i為某個(gè)中間點(diǎn))路P1和P2,P1比P2短,但由于P1到達(dá)的頂點(diǎn)與P2不同,導(dǎo)致最終沿P1到達(dá)t的路徑長(zhǎng)度長(zhǎng)于沿P2到達(dá)t的(甚至有可能沿P1根本到不了t)。然后,如果直接用DFS,由于要求的是第K優(yōu)解,而不是最優(yōu)解,所以不能使用最優(yōu)性剪枝(包括分支限界),因此專門(mén)為最優(yōu)性剪枝服務(wù)的“改變搜索順序”技巧也不能使用了,因此,能夠使用的只有可行性剪枝,而本題的數(shù)據(jù)范圍使得這種算法必然TLE。

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

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

            代碼:
            #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 閱讀(1531) | 評(píng)論 (0)編輯 收藏

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

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

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

            這題的一個(gè)啟示就是,在求類似于“最優(yōu)解計(jì)數(shù)”的問(wèn)題中,不要認(rèn)為當(dāng)后面的狀態(tài)取得最優(yōu)解時(shí),前面的狀態(tài)一定取得最優(yōu)解。因此,不能只記錄某狀態(tài)取得最優(yōu)解的個(gè)數(shù),而要記錄該狀態(tài)取得每一個(gè)可行解時(shí)的個(gè)數(shù)。

            代碼:
            #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 閱讀(819) | 評(píng)論 (0)編輯 收藏

            相關(guān)鏈接

            由此看來(lái),省隊(duì)(準(zhǔn)確來(lái)說(shuō)是“省集訓(xùn)隊(duì)”)的壓力減小很多了囧……因?yàn)榧词故∵x題目再坑爹,也有NOIP的40%墊著……
            顯然,NOIP必須得高分,甩開(kāi)別人,甩得越遠(yuǎn)越好,這樣才能在省集訓(xùn)隊(duì)的選拔中占據(jù)優(yōu)勢(shì)……
            不過(guò),進(jìn)了省集訓(xùn)隊(duì)之后又腫么選,就不知道了……

            而且估計(jì)明年的省選應(yīng)該木有這么坑爹了囧……
            其實(shí)……最近發(fā)現(xiàn)即使是AHOI2012的后3題,只要會(huì)各種搜索+近似也是可以搞到很多分的……至少加起來(lái)100分木問(wèn)題(總分200就A隊(duì)了)……

            所以,實(shí)力終究是硬道理!!!!!!!!!!!!!!!!!!!!!!!!!!!!

            我要進(jìn)省隊(duì)!!!!
            我要進(jìn)國(guó)家集訓(xùn)隊(duì)!!!!

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

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

            代碼:
            #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]數(shù)字序列
            首先,由于序列的所有元素都是整數(shù),所以可以將原序列的所有元素減去它的下標(biāo),這樣就把上升序列轉(zhuǎn)化為不下降序列了。
            第一問(wèn)的結(jié)果顯然就是(N-新序列的最長(zhǎng)不下降序列長(zhǎng)度)。關(guān)鍵在于第二問(wèn)。以下A均表示新序列。
            設(shè)F[i]為以A[i]結(jié)尾的最長(zhǎng)不下降序列長(zhǎng)度(同樣,求法不用說(shuō)了),G[i]為在A[i]不修改的前提下將A[0..i]轉(zhuǎn)變?yōu)椴幌陆敌蛄械淖钚⌒薷牧俊J紫惹蟪鯢[i],然后在求G[i]時(shí),枚舉上一個(gè)“不動(dòng)點(diǎn)”(就是不修改的元素)A[j](顯然必須滿足A[j]<=A[i]且F[j]=F[i]-1),這樣最小修改量就是G[j]+(將A[j..i]轉(zhuǎn)變?yōu)椴幌陆敌蛄械淖钚⌒薷牧浚?梢宰C明,A[j..i]的最優(yōu)修改方案必然是將A[j+1..t]全部修改為A[j],A[t+1..i]全部修改為A[i],這里t是一個(gè)[j..i]范圍的值。問(wèn)題就是如何求出最優(yōu)的t?
            一開(kāi)始,假設(shè)t=j,即把A[j+1..i-1]全部修改為A[i],計(jì)算出修改量,設(shè)為S。然后,由于A[j+1..i-1]之間的元素要么小于A[j],要么大于A[i](這個(gè)是顯然的囧),我們把小于A[j]的元素稱為“小數(shù)”,把大于A[i]的元素稱為“大數(shù)”,則當(dāng)t取t0時(shí),修改量為S-(A[i]-A[j])*(A[j+1..t0]中的“小數(shù)”個(gè)數(shù)減去“大數(shù)”個(gè)數(shù))。這樣,只需掃描一下,求出使得(A[j+1..t0]中的“小數(shù)”個(gè)數(shù)減去“大數(shù)”個(gè)數(shù))值最大的t0即可。
            當(dāng)然還有一個(gè)問(wèn)題,對(duì)于同一個(gè)i,滿足“A[j]<=A[i]且F[j]=F[i]-1”的元素個(gè)數(shù)可能有很多,如果一個(gè)一個(gè)枚舉,一個(gè)一個(gè)掃描,會(huì)很慢的囧……解決方法是,求出滿足這個(gè)條件的j中最小的一個(gè),設(shè)為j0,然后把A[j0+1..i-1]中的所有“小數(shù)”和“大數(shù)”全部處理出來(lái),然后用類似前綴和的方法就能搞了囧……當(dāng)然,為了找到j(luò)0,需要建一個(gè)二分圖,邊為(F[i], i)。
            最后,為了方便,可以把A序列的左邊加一個(gè)-INF,右邊加一個(gè)+INF。最后總的時(shí)間復(fù)雜度,理論上為O(N2),但由于是隨機(jī)數(shù)據(jù),所以遠(yuǎn)遠(yuǎn)達(dá)不到這個(gè)級(jí)別。

            代碼:
            #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 閱讀(1448) | 評(píng)論 (2)編輯 收藏

                 摘要: 原題地址這個(gè)算法是由本沙茶在現(xiàn)場(chǎng)使用的那個(gè)做法擴(kuò)展得來(lái)的……其實(shí)AC不了,后兩個(gè)點(diǎn)會(huì)因?yàn)槌?shù)過(guò)大而T掉……但在BZOJ上算總時(shí)間的話能AC……首先考慮樹(shù)的情形。設(shè)F[i]為從點(diǎn)i開(kāi)始,往子樹(shù)i里面走,到達(dá)葉結(jié)點(diǎn)的期望長(zhǎng)度,則很容易得到遞推公式:F[i] = (ΣF[j] + W(i, j)) / K,其中j是i的子結(jié)...  閱讀全文

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

            原題地址
            本沙茶的第一個(gè)無(wú)向環(huán)套樹(shù)模型,紀(jì)念一下……

            環(huán)套樹(shù),指的是每個(gè)連通塊中點(diǎn)數(shù)都等于邊數(shù)的無(wú)向圖(稱為無(wú)向環(huán)套樹(shù))或者是每個(gè)點(diǎn)有且只有一個(gè)前趨(稱為內(nèi)向環(huán)套樹(shù))或后繼(稱為外向環(huán)套樹(shù))的有向圖,由于這個(gè)圖的每個(gè)連通塊當(dāng)中有且只有一個(gè)環(huán)(注意,可能是自環(huán),即長(zhǎng)度為1的環(huán)),且這個(gè)環(huán)上的每個(gè)點(diǎn)都可以當(dāng)作根引出一棵樹(shù),所以叫“環(huán)套樹(shù)”。

            對(duì)于無(wú)向環(huán)套樹(shù),先將整個(gè)圖進(jìn)行一次DFS,當(dāng)中如果發(fā)現(xiàn)有逆向邊(這條邊第一次被發(fā)現(xiàn)必然是作為逆向邊的,也就是起點(diǎn)是終點(diǎn)的后代),就說(shuō)明找到了這個(gè)環(huán),記錄其起點(diǎn)和終點(diǎn)(注意,如果有多個(gè)連通塊的話,不能退出,要繼續(xù)遍歷完),再不斷上溯(因此在DFS過(guò)程中當(dāng)然要記錄父邊了囧),就可以找到整個(gè)環(huán)了,然后再以環(huán)上的結(jié)點(diǎn)為根建樹(shù)即可,這樣依次處理每個(gè)連通塊。

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

            對(duì)于這題,由于每個(gè)點(diǎn)都有且只有一個(gè)后繼,所以是外向環(huán)套樹(shù),不過(guò)本沙茶更傾向于它的基圖(無(wú)向圖,是無(wú)向環(huán)套樹(shù)),然后就是一個(gè)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 閱讀(1051) | 評(píng)論 (0)編輯 收藏

            最近參加了N多模擬賽……現(xiàn)在統(tǒng)一總結(jié)一下。
            那些有代表性的題目總結(jié)一下。

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

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

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

            (4)Aug.19 [Vani有約會(huì)]杯邀請(qǐng)賽 II
            第二題:本沙茶的80%做法有點(diǎn)另類,是狀態(tài)壓縮DP,因?yàn)閷?duì)于N<=800的數(shù)據(jù),只有2、3、5、7、11、13、17、19、23這9個(gè)質(zhì)數(shù)可能出現(xiàn)兩次以上,其余的都只會(huì)出現(xiàn)一次,所以建立10個(gè)容器(別忘了1),分別分配1和這9個(gè)質(zhì)數(shù),再對(duì)剩下的質(zhì)數(shù)狀態(tài)壓縮即可(一開(kāi)始只建了9個(gè)容器,結(jié)果N=27掛了);

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

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

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

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

            (9)Aug.25 『Citric杯』NOIP提高組模擬賽 II
            第一題:這也太太太太太太太太太太太太太太太太坑爹了吧囧……其實(shí)題目描述有漏洞的,因?yàn)轭}目中并木有說(shuō)表示結(jié)束的詢問(wèn)一定是輸入的最后一個(gè)……
            第三題:這題本沙茶的做法巨另類也巨繁瑣(就當(dāng)字符串算法的綜合復(fù)習(xí)了囧……),首先一頭一尾兩段的字符串還是很好找的……結(jié)尾的那段直接枚舉長(zhǎng)度,開(kāi)頭的的那段可以在整個(gè)字符串左邊用一個(gè)類似KMP的辦法搞(其實(shí)只要知道KMP的原理就能用它解決N多奇特問(wèn)題了囧……),難點(diǎn)在于中間的那段字符串,需要是一個(gè)長(zhǎng)度為奇數(shù)的回文串。為了快速找出一段連續(xù)子串里最長(zhǎng)的長(zhǎng)度為奇數(shù)的回文串,可以設(shè)G[i]為以i為中心的最長(zhǎng)回文串長(zhǎng)度,這可以將整個(gè)字符串逆序一下后接在原串后面(注意中間要加一個(gè)未出現(xiàn)的字符),然后用后綴數(shù)組解決(經(jīng)典模型)。注意在找最長(zhǎng)回文串的時(shí)候不能直接求中間G[i]的最大值,因?yàn)榭赡苎由斐鋈ィ馐嵌置杜e這個(gè)長(zhǎng)度,然后在中間不會(huì)延伸出去的那一段里找G的最大值,看是否符合要求。總時(shí)間復(fù)雜度O(NlogN)。

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

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

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

            從今天開(kāi)始,本沙茶的這個(gè)空間重新啟用。
            ———————————————————————————————————————————————————
            AHOI2012,恥辱的過(guò)去,
            AHOI2013,光明的未來(lái),
            為了自己的集訓(xùn)隊(duì)夢(mèng)想,從今天開(kāi)始,全力復(fù)仇!!!
            ———————————————————————————————————————————————————
            以后這個(gè)空間里的文章題目都會(huì)以【AHOI2013復(fù)仇】為前綴……

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

            僅列出標(biāo)題
            共12頁(yè): 1 2 3 4 5 6 7 8 9 Last 
            久久夜色精品国产亚洲av| 久久精品国产精品亚洲毛片| 中文字幕一区二区三区久久网站| 精品国产青草久久久久福利| 四虎亚洲国产成人久久精品| 久久精品国产精品亚洲人人| 亚洲综合精品香蕉久久网97| 高清免费久久午夜精品| 国产精品久久久久jk制服| 看久久久久久a级毛片| 亚洲AV无码久久精品狠狠爱浪潮| 久久婷婷五月综合国产尤物app| 亚洲人成无码www久久久| 亚洲欧美久久久久9999| 中文字幕精品久久久久人妻| 99久久免费国产精品特黄| 狠狠色丁香久久婷婷综合图片| 色狠狠久久综合网| 久久精品国产精品亚洲精品| 亚洲精品乱码久久久久66| 久久久久亚洲av无码专区| 91精品国产高清久久久久久io | 精品国产VA久久久久久久冰| 久久国产精品99国产精| 国产91久久精品一区二区| 91久久精品国产91性色也| 久久久久久无码国产精品中文字幕 | 日韩欧美亚洲综合久久 | 伊人久久大香线蕉av一区| 香蕉久久av一区二区三区| 91精品国产9l久久久久| 国产叼嘿久久精品久久| 亚洲国产成人精品91久久久| 久久精品国产精品亚洲精品| 久久精品www人人爽人人| 91亚洲国产成人久久精品网址| 欧美粉嫩小泬久久久久久久 | 国产精品美女久久福利网站| 日韩精品久久无码中文字幕| 久久精品国产99国产精偷| 久久久久久亚洲精品无码|