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

            原題見這里(BZOJ和BSOJ都掛了,真杯具,只能借助RQ了囧……)

            難度不是很大,就是特殊情況比較多,比較猥瑣(不過本題數據弱,就算不考慮所有的特殊情況也能過7個點)。
            首先O(NM)的樸素算法很好想到:枚舉K,然后給每個結點編號即可。在編號時,先隨便指定一個未編號的點,設它的編號為0,然后遍歷所有和它相關聯的邊(這里可以把原圖想象成一個無向圖),將這些邊的另一個端點編上號即可,中間若某個點的編號出現矛盾,則這個K不合法,否則這個K合法。
            然后進行優化:合法的K實際上是有限制的,它必然是某個數的因數,具體來說,對這個圖進行DFS,并考察其中所有的跨越邊和逆向邊,對于跨越邊<i, j>,設遍歷樹中i、j間距離為D,則合法的K必然是(D-1)的因數(因為i和遍歷樹中j的父結點都有指向j的邊,它們的編號應相同,而它們之間的距離為(D-1));對于逆向邊<i, j>,設遍歷樹中i、j間距離為D',則合法的K必然是(D'+1)的因數(因為這里形成了一個環,環的長度為(D'+1))。這樣一來就明顯縮小了K的取值范圍,再進行枚舉,就可以顯著縮短時間。

            下面是一些極其猥瑣的特殊情況:
            (1)根據題意,必須是K類每類都有,因此在嘗試編號成功(沒有發生任何矛盾)后,還要看一下實際出現的編號數目是否等于K,若小于K,同樣不合法;
            (2)該圖的基圖可能不連通,此時對于其基圖的每個連通塊,其編號互不影響,所以要對每個連通塊分別統計實際出現的編號數目,設它們的和為SUM,則不大于SUM的K值均合法(只要中間不出現矛盾),因此可以直接得到最大值為SUM,提前結束;不過,這種特判只有在總共實際出現的編號數目小于K的情況下才能進行;
            (3)由于考察的是實際出現的編號數目,因此最后求出的最大值、最小值可能小于3,這時應作出如下處理:若最大值小于3,則無解;若最小值小于3,則將最小值改為3。

            本題比較猥瑣的數據是第4、5、6個點,分別出現了上述的第(1)、(2)、(3)種特殊情況,此外,這三個點建出的圖中竟然沒有一條跨越邊或逆向邊!

            代碼:
            #include <iostream>
            #include 
            <stdio.h>
            using namespace std;
            #define re(i, n) for (int i=0; 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++)
            const int MAXN = 100000, MAXM = 1100001;
            struct edge {
                
            int a, b, s, pre, next;
            } E0[MAXM], E[MAXM 
            + MAXM];
            int n, m0, m, P, len, X[MAXN], No[MAXN], stk[MAXN], st[MAXN], dep[MAXN], V[MAXN], fo[MAXN], Q[MAXN], res0, res1;
            bool vst[MAXN], T0[MAXN];
            long long T[MAXN], _Z = 0;
            void init_d()
            {
                re(i, n) E[i].a 
            = E[i].pre = E[i].next = E0[i].a = E0[i].pre = E0[i].next = i;
                m0 
            = n; if (n % 2) m = n + 1else m = n;
            }
            void add_edge(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++;
                E[m].a 
            = a; E[m].b = b; E[m].s = 1; E[m].pre = E[a].pre; E[m].next = a; E[a].pre = m; E[E[m].pre].next = m++;
                E[m].a 
            = b; E[m].b = a; E[m].s = -1; E[m].pre = E[b].pre; E[m].next = b; E[b].pre = m; E[E[m].pre].next = m++;
            }
            void init()
            {
                freopen(
            "party.in""r", stdin);
                
            int _m, a, b; scanf("%d%d"&n, &_m); init_d();
                re(i, _m) {
                    scanf(
            "%d%d"&a, &b);
                    add_edge(
            --a, --b);
                }
                fclose(stdin);
            }
            int gcd(int a, int b)
            {
                
            int r;
                
            while (b) {
                    r 
            = a % b; a = b; b = r;
                }
                
            return a;
            }
            void prepare()
            {
                
            int tp, x, y, ord = 0;
                
            bool fd;
                re(i, n) V[i] 
            = 0; P = 0;
                re(i, n) 
            if (!V[i]) {
                    stk[tp 
            = 0= i; fo[i] = ord++; V[i] = 1; st[i] = E0[i].next; dep[i] = 0;
                    
            while (tp >= 0) {
                        x 
            = stk[tp]; fd = 0;
                        
            for (int p=st[x]; p != x; p=E0[p].next) {
                            y 
            = E0[p].b;
                            
            if (!V[y]) {
                                stk[
            ++tp] = y; fo[y] = ord++; V[y] = 1; st[y] = E0[y].next; dep[y] = dep[x] + 1; st[x] = E0[p].next; fd = 1break;
                            } 
            else if (V[y] == 1) P = gcd(P, dep[x] - dep[y] + 1); else if (fo[y] > fo[x]) P = gcd(P, dep[y] - dep[x] - 1);
                        }
                        
            if (!fd) {V[x] = 2; tp--;}
                    }
                }
                len 
            = 0; re3(i, 3, n) if (!(P % i)) X[len++= i;
            }
            int test(int K)
            {
                re(i, n) {vst[i] 
            = 0; No[i] = -1;}
                re(i, K) T0[i] 
            = 0;
                
            int x, y, No0, sum = 0, sum0 = 0;
                re(i, n) 
            if (!vst[i]) {
                    No[i] 
            = 0; Q[0= i; vst[i] = 1; _Z++if (T[0!= _Z) {T[0= _Z; sum++;} if (!T0[0]) {T0[0= 1; sum0++;}
                    
            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; No0 = No[x] + E[p].s;
                            
            if (No0 == K) No0 = 0else if (No0 == -1) No0 = K - 1;
                            
            if (No[y] >= 0 && No0 != No[y]) return -1else {
                                No[y] 
            = No0;
                                
            if (T[No0] != _Z) {T[No0] = _Z; sum++;}
                                
            if (!T0[No0]) {T0[No0] = 1; sum0++;}
                            }
                            
            if (!vst[y]) {vst[y] = 1; Q[++rear] = y;}
                        }
                    }
                }
                
            if (sum0 < K) res0 = sum;
                
            return sum0;
            }
            void solve()
            {
                
            int K, K0; res0 = res1 = -1;
                re(i, len) {
                    K 
            = X[i]; K0 = test(K);
                    
            if (K0 != -1) {
                        
            if (res1 == -1) res1 = K0;
                        
            if (K0 < K) breakelse res0 = K;
                    }
                }
                
            if (res0 < 3) res0 = res1 = -1else if (res1 < 3) res1 = 3;
            }
            void pri()
            {
                freopen(
            "party.out""w", stdout);
                printf(
            "%d %d\n", res0, res1);
                fclose(stdout);
            }
            int main()
            {
                init();
                prepare();
                solve();
                pri();
                
            return 0;
            }

            posted @ 2011-07-14 12:23 Mato_No1 閱讀(480) | 評論 (0)編輯 收藏

            世界上已經木有比我還弱的了……

            256:這么水的題竟然搞了30+min,一開始米看見每隔7天就要買同一種,以為是DP,結果測Sample3米過去,于是重寫……(要木有Sample3就杯具了)

            512:由于256上卡了太多時間,這題在快寫完的時候到時間了……

            1024:看都米看……

            結果:全場400+名……估計下次得掉回DIV2了……

            posted @ 2011-07-14 10:04 Mato_No1 閱讀(560) | 評論 (3)編輯 收藏

                 摘要: 【2-SAT問題】現有一個由N個布爾值組成的序列A,給出一些限制關系,比如A[x] AND A[y]=0、A[x] OR A[y] OR A[z]=1等,要確定A[0..N-1]的值,使得其滿足所有限制關系。這個稱為SAT問題,特別的,若每種限制關系中最多只對兩個元素進行限制,則稱為2-SAT問題。由于在2-SAT問題中,最多只對兩個元素進行限制,所以可能的限制關系共...  閱讀全文

            posted @ 2011-07-13 11:51 Mato_No1 閱讀(8728) | 評論 (1)編輯 收藏

            【原題見這里

            本沙茶見過的最猥瑣的DP題啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊……

            設F[i]為將A[1..i]拆分成若干段的最大值最小和,則有
            F[i]=min{F[j] + max[j+1, i]}(B[i]<=j<i),其中max[j+1, i]表示A[j+1..i]中的最大值,B[i]表示從i向左最遠可以延伸到哪里(也就是滿足SUM[x..i]<=m的最小的x值)。B數組可以通過預處理在O(N)時間內得到。
            邊界:F[0]=0。

            下面是優化過程。JZP神犇的論文里面已經夠詳細了。這里只是簡要說明一下。
            首先容易證明,F是單調遞增的。
            然后一個很關鍵的定理是:若F[i]的最優決策為j,則有A[j]>∀A[k](j<k<=i)。
            證明:用反證法。若A[j+1..i]中存在不小于A[j]的值,則可得max[j..i]=max[j+1..i],又因為F單調遞增,所以F[j-1]+max[j..i]<=F[j]+max[j+1.i],即決策(j-1)一定不比決策j差,也就是決策j不可能成為最優決策。
            這樣,可以維護一個下標嚴格遞增、A值嚴格遞減的隊列Q(即對于隊列中的任意兩個元素Q[i]和Q[j],若i<j,則Q[i].pos<Q[j].pos且A[Q[i].pos]>A[Q[j].pos],具體實現時pos可省略)。則可能成為最優決策的決策要么是在這個隊列Q里,要么是B[i]。對于隊列中的某個決策Q[x],該決策導出的值為F[Q[x]]+A[Q[x + 1]](很容易證明max[Q[x]+1..i]=A[Q[x + 1]]),找到這些導出的值中的最小值即可(注意,隊尾元素沒有導出值)。對于決策B[i],只需要在預處理的時候同時得到MAX[i]=max[B[i]+1..i]即可(也可以在O(N)時間內得到),決策B[i]導出的值為F[B[i]]+MAX[i]。
            在刪除隊首過時元素的時候,需要把導出值也刪除,刪除隊尾元素也一樣,插入的時候,若插入前隊列不為空,則需要插入一個導出值。也就是,需要一個支持在對數時間內進行插入、刪除任意結點、找最小值等操作,顯然用平衡樹最好。

            注意事項:
            (1)不管是在隊首刪除還是在隊尾刪除,若刪除的是隊列中的最后一個元素,則不需要在平衡樹中刪除導出值;
            (2)插入時,若插入前隊列為空,則不需要在平衡樹中插入導出值;
            (3)在計算F[i]時,應先將決策i壓入。

            代碼:
            #include <iostream>
            #include 
            <stdio.h>
            using namespace std;
            #define re1(i, n) for (int i=1; i<=n; i++)
            const int MAXN = 100001;
            struct node {
                
            int l, r, p, sz0, sz, mul;
                
            long long v;
            } T[MAXN];
            const long long INF = ~0Ull >> 2;
            int n, N = 0, a[MAXN], b[MAXN], MAX[MAXN], Q[MAXN], front = 0, rear = -1, root = 0;
            long long m, F[MAXN], res = 0;
            void init()
            {
                cin 
            >> n >> m;
                re1(i, n) scanf(
            "%d"&a[i]); a[0= ~0U >> 2;
            }
            void prepare()
            {
                re1(i, n) 
            if (a[i] > m) {res = -1return;}
                
            int x = 1;
                
            long long sum = 0;
                re1(i, n) {
                    
            for (sum+=a[i]; sum>m; sum-=a[x++]) ;
                    b[i] 
            = x - 1;
                }
                re1(i, n) {
                    
            for (; front<=rear && Q[front]<=b[i]; front++) ;
                    
            for (; front<=rear && a[Q[rear]]<=a[i]; rear--) ;
                    Q[
            ++rear] = i; MAX[i] = a[Q[front]];
                }
            }
            void vst(int x)
            {
                
            if (x) {
                    cout 
            << T[x].v << ' ';
                    vst(T[x].l); vst(T[x].r);
                }
            }
            void slc(int _p, int _c)
            {
                T[_p].l 
            = _c; T[_c].p = _p;
            }
            void src(int _p, int _c)
            {
                T[_p].r 
            = _c; T[_c].p = _p;
            }
            void upd(int x)
            {
                T[x].sz0 
            = T[T[x].l].sz0 + T[T[x].r].sz0 + T[x].mul;
                T[x].sz 
            = T[T[x].l].sz + T[T[x].r].sz + 1;
            }
            void lrot(int x)
            {
                
            int y = T[x].p;
                
            if (y == root) T[root = x].p = 0else {int p = T[y].p; if (y == T[p].l) slc(p, x); else src(p, x);}
                src(y, T[x].l); slc(x, y); T[x].sz0 
            = T[y].sz0; T[x].sz = T[y].sz; upd(y);
            }
            void rrot(int x)
            {
                
            int y = T[x].p;
                
            if (y == root) T[root = x].p = 0else {int p = T[y].p; if (y == T[p].l) slc(p, x); else src(p, x);}
                slc(y, T[x].r); src(x, y); T[x].sz0 
            = T[y].sz0; T[x].sz = T[y].sz; upd(y);
            }
            void maintain(int x, bool ff)
            {
                
            int z;
                
            if (ff) {
                    
            if (T[T[T[x].r].r].sz > T[T[x].l].sz) {z = T[x].r; lrot(z);}
                    
            else if (T[T[T[x].r].l].sz > T[T[x].l].sz) {z = T[T[x].r].l; rrot(z); lrot(z);} else return;
                } 
            else {
                    
            if (T[T[T[x].l].l].sz > T[T[x].r].sz) {z = T[x].l; rrot(z);}
                    
            else if (T[T[T[x].l].r].sz > T[T[x].r].sz) {z = T[T[x].l].r; lrot(z); rrot(z);} else return;
                }
                maintain(T[z].l, 
            0); maintain(T[z].r, 1); maintain(z, 0); maintain(z, 1);
            }
            int find(long long _v)
            {
                
            int i = root;
                
            long long v0;
                
            while (i) {
                    v0 
            = T[i].v;
                    
            if (_v == v0) return i; else if (_v < v0) i = T[i].l; else i = T[i].r;
                }
                
            return 0;
            }
            int Find_Kth(int K)
            {
                
            int i = root, s0, m0;
                
            while (1) {
                    s0 
            = T[T[i].l].sz0; m0 = T[i].mul;
                    
            if (K <= s0) i = T[i].l; else if (K <= s0 + m0) return i; else {K -= s0 + m0; i = T[i].r;}
                }
            }
            void ins(long long _v)
            {
                
            if (!root) {
                    T[
            ++N].v = _v; T[N].l = T[N].r = T[N].p = 0; T[N].sz0 = T[N].sz = T[N].mul = 1; root = N;
                } 
            else {
                    
            int i = root, j;
                    
            long long v0;
                    
            while (1) {
                        T[i].sz0
            ++; v0 = T[i].v;
                        
            if (_v == v0) {T[i].mul++return;} else if (_v < v0) j = T[i].l; else j = T[i].r;
                        
            if (j) i = j; else break;
                    }
                    T[
            ++N].v = _v; T[N].l = T[N].r = 0; T[N].sz0 = T[N].sz = T[N].mul = 1if (_v < v0) slc(i, N); else src(i, N);
                    
            while (i) {T[i].sz++; maintain(i, _v > T[i].v); i = T[i].p;}
                }
            }
            void del(int x)
            {
                
            if (T[x].mul > 1) {
                    T[x].mul
            --while (x) {T[x].sz0--; x = T[x].p;}
                } 
            else {
                    
            int l = T[x].l, r = T[x].r, p;
                    
            if (l && r) {
                        
            int y; while (y = T[l].r) l = y;
                        T[x].v 
            = T[l].v; T[x].mul = T[l].mul; p = T[l].p;
                        
            if (l == T[p].l) slc(p, T[l].l); else src(p, T[l].l);
                        
            while (p) {upd(p); p = T[p].p;}
                    } 
            else {
                        
            if (x == root) T[root = l + r].p = 0else {p = T[x].p; if (x == T[p].l) slc(p, l + r); else src(p, l + r); while(p) {upd(p); p = T[p].p;}}
                    }
                }
            }
            long long h(int x)
            {
                
            return F[Q[x]] + a[Q[x + 1]];
            }
            void solve()
            {
                F[
            0= 0; front = 0; rear = 0; Q[0= 0;
                re1(i, n) {
                    
            for (; front<=rear && Q[front]<b[i];) {if (front < rear) del(find(h(front))); front++;}
                    
            for (; front<=rear && a[Q[rear]]<=a[i];) {if (front < rear) del(find(h(rear - 1))); rear--;}
                    Q[
            ++rear] = i; if (front < rear) ins(h(rear - 1));
                    
            if (root) F[i] = T[Find_Kth(1)].v; else F[i] = INF;
                    
            if (F[b[i]] + MAX[i] < F[i]) F[i] = F[b[i]] + MAX[i];
                }
                res 
            = F[n];
            }
            void pri()
            {
                cout 
            << res << endl;
            }
            int main()
            {
                init();
                prepare();
                
            if (!res) solve();
                pri();
                
            return 0;
            }

            posted @ 2011-07-08 12:40 Mato_No1 閱讀(495) | 評論 (1)編輯 收藏

            多重背包問題樸素時間復雜度為O(NMS)(這里S是所有物品的數量s之和),經過二進制優化后時間復雜度為O(NMlog2S),這個復雜度已經能夠應付大多數題了,但對于某些特別卡時間的題(比如N*M=107的),仍然會TLE。這時,可以用單調隊列優化,時間復雜度降為O(NM)。

            首先看一下多重背包問題的樸素轉移方程:
            F[i][j] = max{F[i-1][j-x*w[i]]+x*v[i]} (0<=x<=s[i], j>=x*w[i])
            如果使用滾動數組,忽略i這一維,設w0=w[i],v0=v[i],s0=s[i],得:
            F[j] = max{F[j-x*w0]+x*v0} (0<=x<=s0, j>=x*w0)
            看上去這和單調隊列木有神馬關系,因為決策下標(j-x*w0)不是一個整數區間,中間是有間隔的。然而可以發現,這個方程的限制條件“0<=x<=s0,j>=x*w0”,也就是x的下界是max{0, j/w0(下取整)},當j單調遞增時,這個下界也是單調遞增的。這滿足單調隊列優化的條件中的“決策下標的下界單調”……不是,還不能這樣說,因為這里的決策下標是j-x*w0,而不是x。
            那么怎樣才可以把決策下標變為x?

            將決策下標按照模w0的余數進行分類,可以分成w0類,分別對應模w0余0、余1……余(w0-1)的情況。這時,上面方程中的所有決策下標j-x*w0都是同一類的。進一步,設q =j/w0(下取整),r=j%w0,則j=q*w0+r,對于某個決策下標j',設k=(j'-r)/w0,即j'=k*w0+r。顯然可以發現,k的取值范圍是:k>=0且q-s0<=k<=q,也即k的下界是max{0, q-s0}——隨j的單調而單調。
            然后,轉移方程可以改為(這里把r當成一個已知量了):
            F[q*w0+r] = max{F[k*w0+r]+(q-k)*v0} (k>=0且q-s0<=k<=q)
            即F[q*w0+r]=max{F[k*w0+r]-k*v0}+q*v0 (k>=0且q-s0<=k<=q)
            設G[k]=F[k*w0+r]得:
            G[q]=max{G[k]-k*v0}+q*v0 (k>=0且q-s0<=k<=q)
            這個方程已經可以使用單調隊列來優化了!

            這樣可以得出算法:
            (1)從1到n,枚舉i,建立w[i]個空的單調隊列,每個隊列的元素都是兩個int值:(k, val),表示轉換后下標和決策值(G[k]-k*v[i]);
            (2)從0到m,枚舉j,得出q、r的值,對于隊列r:
            【1】刪去隊首過時(k<q-m[i])的元素;
            【2】F[j]入隊(這里的F[j]指上一階段的F[j],即F[i-1][j]。因此這一步操作一定要先進行),刪去隊尾所有決策值val不大于(F[j]-q*v[i])的元素。
            【3】取出隊首結點,其val值加上q*v[i]后即為本階段F[j]的值。
            最后F[m]即為結果??倳r間復雜度為O(NM)。

            其實這個是可以推廣的,即對于如下形式的轉移方程(其中H、G和W均為常量,B[i]為決策下標的下界,隨i單調):
            F[i] = opt{F[i-x*H+W]}+G (B[i]<=i-x*H+W<i,x∈N
            都可以用上述的辦法進行轉化,從而進行單調隊列優化。

            posted @ 2011-07-05 18:00 Mato_No1 閱讀(5999) | 評論 (3)編輯 收藏

            My first SRM……紀念一下

            這次總體感覺還不是太差,也算正常發揮了——雖然最后還是米有搞定1000。250和500兩道水題的速度應該還可以(從最終名次來看)。
            另外,DIV2全場只有2位神犇搞定了1000……

            Orz AHdoc等神犇
            ————————————————————————————————————
            以下為1000題解(看別的神犇的代碼搞懂的):
            設F[i][j]為第i輪開始時(還未出牌時),面對的狀態(內存)為j,是否必勝。這里設一開始的那一輪為第0輪。
            逆推i。根據or運算的性質可以得到,若目前內存為j,某張已經出過的牌的值為K,則K的二進制的所有1位在j中對應的位也都是1(也就是j | K = j),這樣,掃描每張牌,若其值K | j的值不等于j,則該牌不可能出過。因此,可以在第i輪出這張牌,若至少有一個F[i + 1][K | j]為必敗狀態則F[i][j]為必勝狀態。
            對于K | j的值等于j的牌,統計它們的張數,設為cnt。易知,前i輪出過的i張牌必然都是這種牌,因此若cnt>i,且F[i + 1][j]是必敗狀態,則可以在第i輪出一張這樣的牌,必勝。
            如果上面沒有發現一個可以使F[i][j]為必勝狀態的,則F[i][j]為必敗狀態。
            邊界:F[i][511]為必勝狀態(0<=i<=N),F[N][j]為必敗狀態(0<=j<511,因為第N輪時已經木有牌了)。
            最后,若F[0][0](初始狀態)為必勝狀態則先手必勝,否則先手必敗。

            posted @ 2011-07-03 02:09 Mato_No1 閱讀(523) | 評論 (0)編輯 收藏

            矩形的面積并問題:平面上有N個矩形,各邊均平行于坐標軸,求它們覆蓋的總面積(重復覆蓋的只計一次)。
            矩形的周長并問題:平面上有N個矩形,各邊均平行于坐標軸,求它們覆蓋形成的多邊形的周長。

            【算法】
            面積并:
            先將所有矩形的上邊界和下邊界作為水平線段記錄下來,并對所有矩形的左右邊界對應的橫坐標離散化,設離散化后有N個橫坐標,則中間有(N-1)段。對這(N-1)段建立線段樹(注意,仍然和普通線段樹一樣,是雙閉區間,不是網上說的一開一閉),然后,按照縱坐標遞增順序掃描前面記錄的水平線段(設有M段),對每一段,如果是上邊界,找到其離散化后的范圍(只需找到其左右端點離散化后的值l、r,則對應范圍為[l, r-1]),并插入線段[l, r-1],否則(下邊界),刪除線段[l, r-1]。再然后,線段樹中的每個結點需要記錄該區間內的線段覆蓋的總長度len(若該區間被某條尚未刪除的線段整體覆蓋,則len=總長,否則len=左右子結點len之和),每次操作后,累加面積:T[root].len*該水平線段與下一條水平線段的縱坐標之差。

            周長并:
            類似,只不過由于組成周長的線段有水平的也有豎直的,線段樹結點要記錄的除了len意外還有一個ss,表示被線段覆蓋的端點數量。另外還有lr和rr兩個bool值,分別表示該線段的左端點和右端點是否被某條插入的線段覆蓋。則T[x].ss = lch(T[x]).ss + rch(T[x]).ss - 2 * (lch(T[x]).rr && rch(T[x].lr)),若該線段被整體覆蓋則T[x].ss=2(兩端點)。最后,這次得到的T[root].len與上次得到的T[root].len之差的絕對值就是水平線段的長度,T[root].ss*縱坐標之差就是豎直線段的長度。

            posted @ 2011-07-02 11:17 Mato_No1 閱讀(2763) | 評論 (0)編輯 收藏

            My first CF……慘掛,痛定思痛……
            最終就過了2題(BD),
            C題竟然把題意搞疵了,這里deselect是抵消的意思……也就是選的矩形是不能相交的……米搞懂……
            A題,最后測試的時候掛了……原來是結果用int存儲,忘了可能有前導0……
            E題更是連看都米來得及看……
            …………………………………………
            DIV1是神犇組,DIV2是沙茶組,我在沙茶組里都被虐成這個樣子,因此我已經淪為“沙茶中的沙茶”了……

            Orz CLJ神犇、AHdoc神犇、xiaodao神犇……

            posted @ 2011-07-01 09:51 Mato_No1 閱讀(613) | 評論 (0)編輯 收藏

            相關鏈接

            Orz AHdoc!!!!!!!!!!!!!

            這種神犇算法的關鍵在于真正利用了MST是一棵“樹”的性質。也就是,它在求出MST后把它轉化為有根樹,然后,按長度遞增順序對于圖中每一條不在MST中的邊(i, j),找到樹中i、j的最近公共祖先(LCA),記為p=LCA(i, j)。這樣,樹中i->p->j就是從i到j的路徑。然后,依次掃描這條路徑上的所有的邊,將新邊(i, j)的長度與路徑上所有邊的長度比較,找到長度差最小的(不過由于邊(i, j)的長度一定不小于路徑上所有邊的長度,所以只要找到路徑上最長邊,則“刪去這條最長邊,加入邊(i, j)”一定是所有加入的邊為(i, j)的可行變換中代價最小的),取這個長度差最小的即可。不過最為神犇的一點是,這個算法在遍歷完這條路徑后,會將路徑上所有的點(p點除外)的父結點全部設為p,也就是相當于并查集的路徑壓縮!這雖然會改變樹的形態,但任何兩點的LCA都是不會變的,因此不會影響后面的邊。

            注意上面“按長度遞增順序”是重點,原因是“路徑壓縮”可能會改變某些點之間的路徑,也就是將某些點之間的路徑長度減小。但是,很容易發現,被“壓縮”的這些邊必然是已經訪問過的,也就是說這些邊必然已經作為了前面的某條邊(i, j),i到j路徑上的邊。對于這條邊來說,可行變換中,新加入的邊的長度應盡量小,因此,如果按長度遞增順序,則這些邊在(i, j)之后肯定不會出現代價更小的可行變換,因此就可以將它們壓縮,不會影響最優解。

            復雜度分析:先不管求LCA的時間復雜度。設樹中結點i的深度為h[i](h[root]=0)。對于樹中的任意一個葉結點v,從root到v的路徑的總長度(總邊數)為h[v],因此,若某次要嘗試的邊(i, j)的某一端點(設為i)在從root到v的這條路徑上,則p=LCA(i, j)一定也在這條路徑上。這樣,訪問從i到p的路徑上的總訪問次數(也就是從i到p路徑上的邊數)為(h[i]-h[p])。在訪問完成后,需要將從i到p路徑上除p外的所有結點的父結點都設為p,也就是從root到v的路徑的總長度減少了(h[i]-h[p]-1)。因此,在嘗試所有不在MST中的邊的過程中,訪問從root到v的最初路徑上的邊的總次數不會超過(h[v]+這些邊的總數)(這里h[v]指初始的h[v])。因此可以得到:訪問樹中所有邊的總次數不會超過(最初所有葉結點深度之和+2*M),M為所有不在MST中邊的總數!由于“最初所有葉結點深度之和”不會超過Nlog2N,因此總時間復雜度為O(Mlog2M+M+Nlog2N),其中O(Mlog2M)為用Kruskal求MST的時間,如果忽略這部分時間,則總時間復雜度為O(M+Nlog2N)。
            其實這個算法的時間復雜度在忽略排序的情況下是線性的,即O(M+N),但本沙茶搞不懂怎么證明這一步。

            下面是具體實現時的注意事項:
            (1)將MST轉化為有根樹時,應用BFS,而不要用DFS,否則對于特殊數據可能爆棧;
            (2)求LCA時,應用先讓深度大的結點向上的方法(AHdoc神犇的方法),具體見下面的代碼片段1;或者應用兩者同時往上的方法(本沙茶的方法),具體見下面的代碼片段2;否則,對于樹是一條鏈,且每次訪問都是訪問最深的兩個結點時,一次求LCA的時間復雜度可能升到O(N)。

            【代碼片段1】
            int lca(int a, int b)
            {
                
            for (;;)
                {
                    
            if (a == b) return b;
                    
            if (h[a] >= h[b]) a = Fa[a]; else b = Fa[b];
                }
            }
            【代碼片段2】
            int LCA(int x, int y)
            {
                
            while (x && y) {
                    
            if (fl[x] == _s) return x; else fl[x] = _s;
                    
            if (fl[y] == _s) return y; else fl[y] = _s;
                    x 
            = pr[x]; y = pr[y];
                }
                
            if (x) while (1if (fl[x] == _s) return x; else x = pr[x]; else while (1if (fl[y] == _s) return y; else y = pr[y];
            }

            【具體題目】Beijing2010 Tree(BZOJ1977)
            這題要求嚴格次小生成樹,因此在枚舉的時候要注意,不能使可行變換的代價為0。
            #include <iostream>
            #include 
            <stdio.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++)
            const int MAXN = 100001, MAXM = 300001;
            const long long INF = ~0Ull >> 2;
            struct edge {
                
            int a, b, len;
                friend 
            bool operator< (edge e0, edge e1) {return e0.len < e1.len;}
            } E[MAXM];
            struct edge0 {
                
            int a, b, id, pre, next;
            } E0[MAXM 
            + MAXM];
            int n, m, m0, u[MAXN], pr[MAXN], No[MAXN], s[MAXM], Q[MAXN], fl[MAXN], _s;
            long long mst_v = 0, res;
            bool inmst[MAXM], vst[MAXN];
            void init()
            {
                scanf(
            "%d%d"&n, &m);
                re(i, m) scanf(
            "%d%d%d"&E[i].a, &E[i].b, &E[i].len);
            }
            int find(int x) {int r = x, r0; while (u[r] > 0) r = u[r]; while (u[x] > 0) {r0 = u[x]; u[x] = r; x = r0;} return r;}
            void uni(int s1, int s2) {int tmp = u[s1] + u[s2]; if (u[s1] > u[s2]) {u[s1] = s2; u[s2] = tmp;} else {u[s2] = s1; u[s1] = tmp;}}
            void init_d()
            {
                re1(i, n) {E0[i].a 
            = i; E0[i].pre = E0[i].next = i;}
                
            if (n % 2) m0 = n + 1else m0 = n + 2;
            }
            void add_edge(int a, int b, int id)
            {
                E0[m0].a 
            = a; E0[m0].b = b; E0[m0].id = id; 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].id = id; E0[m0].pre = E0[b].pre; E0[m0].next = b; E0[b].pre = m0; E0[E0[m0].pre].next = m0++;
            }
            void prepare()
            {
                sort(E, E 
            + m);
                re1(i, n) u[i] 
            = -1; init_d();
                
            int s1, s2, z = 0;
                re(i, m) {
                    s1 
            = find(E[i].a); s2 = find(E[i].b);
                    
            if (s1 != s2) {z++; mst_v += E[i].len; add_edge(E[i].a, E[i].b, i); inmst[i] = 1; uni(s1, s2); if (z == n - 1break;}
                }
            }
            void bfs()
            {
                re1(i, n) vst[i] 
            = 0;
                Q[
            0= 1; vst[1= 1;
                
            int i, j;
                
            for (int 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 (!vst[j]) {
                            vst[j] 
            = 1; Q[++rear] = j; pr[j] = i; No[j] = E0[p].id;
                        }
                    }
                }
            }
            int LCA(int x, int y)
            {
                
            while (x && y) {
                    
            if (fl[x] == _s) return x; else fl[x] = _s;
                    
            if (fl[y] == _s) return y; else fl[y] = _s;
                    x 
            = pr[x]; y = pr[y];
                }
                
            if (x) while (1if (fl[x] == _s) return x; else x = pr[x]; else while (1if (fl[y] == _s) return y; else y = pr[y];
            }
            void sol0(int a, int b, int l)
            {
                
            int p = LCA(a, b), p0, No0;
                
            while (a != p) {No0 = No[a]; if (!s[No0] && l > E[No0].len) s[No0] = l - E[No0].len; p0 = pr[a]; pr[a] = p; a = p0;}
                
            while (b != p) {No0 = No[b]; if (!s[No0] && l > E[No0].len) s[No0] = l - E[No0].len; p0 = pr[b]; pr[b] = p; b = p0;}
            }
            void solve()
            {
                pr[
            1= 0; bfs();
                re(i, m) 
            if (!inmst[i]) {_s = i + 1; sol0(E[i].a, E[i].b, E[i].len);}
                res 
            = INF;
                re(i, m) 
            if (inmst[i] && s[i] && s[i] < res) res = s[i];
                res 
            += mst_v;
            }
            void pri()
            {
                cout 
            << res << endl;
            }
            int main()
            {
                init();
                prepare();
                solve();
                pri();
                
            return 0;
            }

            posted @ 2011-07-01 09:10 Mato_No1 閱讀(2757) | 評論 (8)編輯 收藏

            在沒有修改操作時,應用劃分樹可以在O(MlogN)時間內解決查找區間第K小的問題,但是在引入修改(將原序列中的某個值改為另一個值)之后,劃分樹就不行了。
            這時,需要數據結構聯合的思想。
            可以觀察一下:
            (1)區間操作:使用線段樹;
            (2)修改值(其實是先刪除再插入)和找第K?。菏褂闷胶鈽洌?br />現在這兩種操作都有,應該使用線段樹+平衡樹!
            準確來說是線段樹套平衡樹,即對原序列建立一棵線段樹,其中的每個結點內套一棵對該結點管轄區間內的平衡樹。

            <1>結點類型(結構):
            struct seg_node {
                
            int l, r, mid, lch, rch, rt;
            } T0[MAXN0];
            struct SBT_node {
                
            int v, l, r, p, sz0, sz, mul;
            } T[MAXN];
            其中seg_node是線段樹結點類型,SBT_node是平衡樹(SBT)結點類型。需要注意的是seg_node里面的rt域(root的縮寫),它是該結點內套的平衡樹的根結點下標索引(因為對于任意一棵平衡樹,只要知道了其根結點就可以遍歷整棵樹)。

            <2>建樹:
            建樹是線段樹和平衡樹一起建。在建立線段樹結點的時候,先建立一棵空的平衡樹(rt域置0),然后再在平衡樹里面逐個插入該結點管轄區間內的所有元素即可;

            <3>修改:
            修改操作要注意:如果要將A[x](A為原序列)的值修改為y,則需要自頂向下遍歷整棵線段樹,將所有包含了A[x]的結點內的平衡樹全部執行“刪除v=A[x](這個可以通過真正維護一個序列得到),再插入y”的操作;

            <4>找區間第K?。?br />這個操作極其麻煩。需要借助二分。
            設要在區間[l, r]中找到第K小。首先將[l, r]拆分成若干個線段樹結點,然后二分一個值x,在這些結點的平衡樹中找到x的rank(這里的rank指平衡樹中有多少個值比x小,不需要加1),加起來,最后再加1,就是x在[l, r]中的總名次。問題是,設[l..r]中第K小的數為v1,第(K+1)小的數為v2(如果不存在的話,v2=+∞),則[v1, v2)內的數都是“第K小”的。因此,不能二分數字,而應該二分元素。設S[i]為原序列中第i小的數,二分i,然后在根結點的平衡樹中找到第i小的即為S[i],再求其名次,這樣直到找到總名次為K的元素為止。問題還沒完,序列中可能有元素的值相同,這時可能永遠也找不到第K小的(比如序列1 2 3 3 3 4 5,K=4,若“序列中比x小的元素總數+1”為x的名次,則永遠也找不到第4小的),因此,若這樣求出的“名次”小于等于K,都應該將下一次的左邊界設為mid而不是(mid+1),而“名次”大于K時,該元素肯定不是第K小的,所以下一次右邊界設為(mid-1)。

            代碼(本機測最猥瑣數據4s以內,交到ZJU上TLE,不知為什么,神犇指點一下,3x):
            #include <iostream>
            #include 
            <stdio.h>
            using namespace std;
            #define re(i, n) for (int i=0; i<n; i++)
            #define re3(i, l, r) for (int i=l; i<=r; i++)
            const int MAXN0 = 110000, MAXN = 930000, INF = ~0U >> 2;
            struct seg_node {
                
            int l, r, mid, lch, rch, rt;
            } T0[MAXN0];
            struct SBT_node {
                
            int v, l, r, p, sz0, sz, mul;
            } T[MAXN];
            int No0, No, n, root, rt0, a[MAXN0 >> 1], b[MAXN0 >> 1], l1, r1, len;
            void slc(int _p, int _c)
            {
                T[_p].l 
            = _c; T[_c].p = _p;
            }
            void src(int _p, int _c)
            {
                T[_p].r 
            = _c; T[_c].p = _p;
            }
            void upd(int x)
            {
                T[x].sz0 
            = T[T[x].l].sz0 + T[T[x].r].sz0 + T[x].mul;
                T[x].sz 
            = T[T[x].l].sz + T[T[x].r].sz + 1;
            }
            void lrot(int x)
            {
                
            int y = T[x].p; if (y == rt0) T[rt0 = x].p = 0else {int p = T[y].p; if (y == T[p].l) slc(p, x); else src(p, x);}
                src(y, T[x].l); slc(x, y); T[x].sz0 
            = T[y].sz0; T[x].sz = T[y].sz; upd(y);
            }
            void rrot(int x)
            {
                
            int y = T[x].p; if (y == rt0) T[rt0 = x].p = 0else {int p = T[y].p; if (y == T[p].l) slc(p, x); else src(p, x);}
                slc(y, T[x].r); src(x, y); T[x].sz0 
            = T[y].sz0; T[x].sz = T[y].sz; upd(y);
            }
            void maintain(int x, bool ff)
            {
                
            int z;
                
            if (ff) {
                    
            if (T[T[T[x].r].r].sz > T[T[x].l].sz) {z = T[x].r; lrot(z);}
                    
            else if (T[T[T[x].r].l].sz > T[T[x].l].sz) {z = T[T[x].r].l; rrot(z); lrot(z);} else return;
                } 
            else {
                    
            if (T[T[T[x].l].l].sz > T[T[x].r].sz) {z = T[x].l; rrot(z);}
                    
            else if (T[T[T[x].l].r].sz > T[T[x].r].sz) {z = T[T[x].l].r; lrot(z); rrot(z);} else return;
                }
                maintain(T[z].l, 
            0); maintain(T[z].r, 1); maintain(z, 0); maintain(z, 1);
            }
            int find(int _v)
            {
                
            int i = rt0, v0;
                
            while (i) {
                    v0 
            = T[i].v;
                    
            if (_v == v0) return i; else if (_v < v0) i = T[i].l; else i = T[i].r;
                }
                
            return 0;
            }
            void ins(int _v)
            {
                
            if (!rt0) {
                    T[
            ++No].v = _v; T[No].l = T[No].r = T[No].p = 0; T[No].sz0 = T[No].sz = T[No].mul = 1; rt0 = No;
                } 
            else {
                    
            int i = rt0, j, v0;
                    
            while (1) {
                        T[i].sz0
            ++; v0 = T[i].v;
                        
            if (_v == v0) {T[i].mul++return;} else if (_v < v0) j = T[i].l; else j = T[i].r;
                        
            if (j) i = j; else break;
                    }
                    T[
            ++No].v = _v; T[No].l = T[No].r = 0; T[No].sz0 = T[No].sz = T[No].mul = 1if (_v < v0) slc(i, No); else src(i, No);
                    
            while (i) {T[i].sz++; maintain(i, _v > T[i].v); i = T[i].p;}
                }
            }
            void del(int x)
            {
                
            if (T[x].mul > 1) {
                    T[x].mul
            --;
                    
            while (x) {T[x].sz0--; x = T[x].p;}
                } 
            else {
                    
            int l = T[x].l, r = T[x].r;
                    
            if (!|| !r) {
                        
            if (x == rt0) T[rt0 = l + r].p = 0else {
                            
            int p = T[x].p; if (x == T[p].l) slc(p, l + r); else src(p, l + r);
                            
            while (p) {T[p].sz0--; T[p].sz--; p = T[p].p;}
                        }
                    } 
            else {
                        
            int i = l, j;
                        
            while (j = T[i].r) i = j;
                        T[x].v 
            = T[i].v; T[x].mul = T[i].mul; int p = T[i].p; if (i == T[p].l) slc(p, T[i].l); else src(p, T[i].l);
                        
            while (p) {upd(p); p = T[p].p;}
                    }
                }
            }
            int Find_Kth(int K)
            {
                
            int i = rt0, s0, m0;
                
            while (i) {
                    s0 
            = T[T[i].l].sz0; m0 = T[i].mul;
                    
            if (K <= s0) i = T[i].l; else if (K <= s0 + m0) return T[i].v; else {K -= s0 + m0; i = T[i].r;}
                }
            }
            int rank(int _v)
            {
                
            int i = rt0, tot = 0, v0;
                
            while (i) {
                    v0 
            = T[i].v;
                    
            if (_v == v0) {tot += T[T[i].l].sz0; return tot;} else if (_v < v0) i = T[i].l; else {tot += T[T[i].l].sz0 + T[i].mul; i = T[i].r;}
                }
                
            return tot;
            }
            int mkt(int l, int r)
            {
                T0[
            ++No0].l = l; T0[No0].r = r; int mid = l + r >> 1; T0[No0].mid = mid; rt0 = 0;
                re3(i, l, r) ins(a[i]); T0[No0].rt 
            = rt0;
                
            if (l < r) {int No00 = No0; T0[No00].lch = mkt(l, mid); T0[No00].rch = mkt(mid + 1, r); return No00;} else {T0[No0].lch = T0[No0].rch = 0return No0;}
            }
            void fs(int x)
            {
                
            if (x) {
                    
            int l0 = T0[x].l, r0 = T0[x].r;
                    
            if (l0 >= l1 && r0 <= r1) b[len++= T0[x].rt; else if (l0 > r1 || r0 < l1) returnelse {fs(T0[x].lch); fs(T0[x].rch);}
                }
            }
            void C(int x, int _v)
            {
                
            int i = root, l0, r0, mid0, v0 = a[x], N;
                
            while (i) {
                    l0 
            = T0[i].l; r0 = T0[i].r; mid0 = T0[i].mid; rt0 = T0[i].rt;
                    N 
            = find(v0); del(N); ins(_v); T0[i].rt = rt0;
                    
            if (x <= mid0) i = T0[i].lch; else i = T0[i].rch;
                }
                a[x] 
            = _v;
            }
            int Q(int K)
            {
                len 
            = 0; fs(root);
                
            int ls = 1, rs = n, mids, midv, tot;
                
            while (ls < rs) {
                    mids 
            = ls + rs + 1 >> 1; rt0 = T0[root].rt; midv = Find_Kth(mids);
                    tot 
            = 1; re(i, len) {rt0 = b[i]; tot += rank(midv);}
                    
            if (tot <= K) ls = mids; else rs = mids - 1;
                }
                rt0 
            = T0[root].rt; return Find_Kth(ls);
            }
            int main()
            {
                
            int tests, m, x, y, K;
                
            char ch;
                scanf(
            "%d"&tests);
                re(testno, tests) {
                    scanf(
            "%d%d"&n, &m); No0 = No = 0;
                    re(i, n) scanf(
            "%d"&a[i]); ch = getchar();
                    root 
            = mkt(0, n - 1);
                    re(i, m) {
                        ch 
            = getchar();
                        
            if (ch == 'C') {
                            scanf(
            "%d%d%*c"&x, &y);
                            C(
            --x, y);
                        } 
            else {
                            scanf(
            "%d%d%d%*c"&l1, &r1, &K);
                            l1
            --; r1--; printf("%d\n", Q(K));
                        }
                    }
                }
                
            return 0;
            }

            posted @ 2011-06-27 21:53 Mato_No1 閱讀(2515) | 評論 (0)編輯 收藏

            僅列出標題
            共12頁: First 4 5 6 7 8 9 10 11 12 
            99精品国产99久久久久久97| 蜜臀av性久久久久蜜臀aⅴ| 亚洲国产一成久久精品国产成人综合 | 人妻无码精品久久亚瑟影视| 99久久精品免费看国产一区二区三区| 国产三级久久久精品麻豆三级| 青青久久精品国产免费看| 久久99热精品| 久久精品国产99国产精品亚洲| 精品无码久久久久久久动漫| 久久精品蜜芽亚洲国产AV| 亚洲美日韩Av中文字幕无码久久久妻妇| 久久大香香蕉国产| 亚洲日本久久久午夜精品| 精品综合久久久久久97超人| yy6080久久| 久久国产精品无码网站| 国产精品久久久久无码av| 精品国产乱码久久久久久人妻| 精品水蜜桃久久久久久久| 久久久青草久久久青草| 伊人久久大香线焦AV综合影院| 一级女性全黄久久生活片免费| 99久久国产亚洲高清观看2024| AV无码久久久久不卡网站下载| 久久99久国产麻精品66| 一级a性色生活片久久无 | 成人免费网站久久久| 一本久道久久综合狠狠躁AV| 国产999精品久久久久久| 国产成人久久激情91| 久久久久久久久无码精品亚洲日韩 | 欧美亚洲另类久久综合婷婷| 国产成人无码精品久久久久免费 | 伊人色综合久久| 久久精品国产69国产精品亚洲| 久久免费的精品国产V∧| 人妻丰满AV无码久久不卡| 久久久久亚洲av无码专区 | 人人狠狠综合久久亚洲| 久久久久国产亚洲AV麻豆|