1. 程式人生 > >「泛做題解」曾經的 BZOJ 題解匯總

「泛做題解」曾經的 BZOJ 題解匯總

lis 對角線 oid 合數 code 成了 bzoj2333 oot 無法

註:此博客寫於 2017.12

一個利益至上的OJ.

堅決抵制

SDOI

BZOJ3122-[SDOI2013]隨機數生成器 給定參數 \(a,b,X[1]\) ,序列 \(X\)\(X[i+1] = (a \times X[i] + b) \mod p\) 得到。問第一次在序列 \(X\) 中出現 \(t\) 的位置, \(p\) 是一個給定的質數。或者永遠不會出現。

分類討論,大小步算法。 先要特判一波。 \(X[1] = t\)\(a = 0\) 時直接輸出。 \(a = 1\) 時,就是一個裸的 \(exgcd\) 。接下來就怒推一發公式。。。

顯然可以暴力展開,得到:

\[X[n] = aX[n-1] + b = a(aX[n-2] + b) + b = ... = a ^ {n-1} X[1] + (a ^ 0 + a ^ 1 + ... + a ^ {n-2}) b\]

隨便化簡一下:

\[a ^ {n-1} = \frac {X[n] - \frac {a ^ {n-1} -1} { a - 1} \times b} {X[1]} \]

跑個BSGS就好了。小心爆int。

BZOJ1927-[SDOI2010]星際競速 給定一個DAG,要求用若幹條不相交路徑覆蓋所有的點,給定所有邊的代價,以及所有點作為路徑的第一個點的代價。問最小的代價之和。

最小路徑覆蓋,費用流,拆點。

首先考慮 \(S\)\(i\) 連上 \((1,ai)\) 的邊,\(i\)\(T\)\((1,0)\) 。此時它可以選擇 一個 後繼節點,於是考慮增加一個點 \(i+n\) ,需要 \(S \rightarrow n+i, (1,0)\) ,考慮刪除這個點 \(i\) ,對於 \(i \rightarrow j\)\((1,w_{i,j})\) 。表示將 \(j\) 作為第一個點的代價 可以是 \(w_{i,j}\) 。以上是思考過程。

正確性?考慮到每個點的流量只有兩種可能來源,逆推即可得到一種對應方案。同時每一種方案也對應一種可行的流。所以最小費用即為最小答案。

BZOJ1974-[SDOI2010]auction代碼拍賣會

一個 \(10\) 進制表示的正整數,如果從左到右,每一位的數字都不小於前一位的數字,則被稱為上升數。 給出正整數 \(n\)\(K\) ,求有多少個長度恰好為\(n\) 的上升數是 \(K\) 的倍數,答案對 \(999911659\) 取模。 \(n \leq 10^{18}, K \leq 500\)

動態規劃。 如果縱向切割,顯然很難搞。考慮橫向切割,不難發現這個數可以拆分為不超過 \(9\) 個,\(1,11,111,1111,..\) 的和。然後,這些數模 \(K\) 很顯然是存在循環的。定義 \(f[i][j][k]\) 表示前 \(i\) 組中,選擇了 \(j\) 個數,模意義下為 \(K\) 的方案數。

需要註意的是 \(\frac {10^{n-1} - 1} 9\) 要特殊處理,同時註意 \(K\) 等於 \(1\) 的情況

ZJOI

BZOJ1414-[ZJOI2009]對稱的正方形 給定一個 \(n \times m\) 的矩陣。問存在多少子矩陣,使得它上下對稱且左右對稱。
\(n,m \leq 10^3\)

HASH亂搞。 顯然,對於每一個位置,我們可以二分求出一個極大子矩陣。判斷就可以用二維HASH,註意方向,小心不要寫成旋轉對稱QAQ。新技能get!二維哈希。

我們可以弄兩個不同的大素數 \(p,q\)\((i,j)\) 就要乘上 \(p ^ i \times q ^ j\) 。用unsigned int,然後自然溢出就好了。。

不放心的話,可以用unsigned long long。相當於雙模數HASH

BZOJ2111-[ZJOI2010]Perm 排列計數 計算 \(1,2,..,n\) 的排列在大根堆中有多少種方案。

組合,動態規劃。 首先求出每個位置的 \(size\) ,定義 \(f[i]\) 為以第 \(i\) 個位置為根的方案數。如果它有兩個兒子,那麽 \(f[i] = \binom {size[i]-1} {size[lc]} \times f[lc] \times f[rc]\)

註意,這裏的組合數的 \(n,m\) 有可能超過 \(MOD\) 。所以需要使用 Lucas定理

SHOI

BZOJ1018-[SHOI2008]堵塞的交通traffic 給定一個 \(2*n\) 的網格圖,要求支持加邊,刪邊,求兩點的連通。

線段樹。 因為是線段上的操作,所以很顯然可以用線段樹來維護。

不難發現,所有的路徑都像這樣:向往另一端走,然後轉回來,超過之後再轉回來。所以就要維護前綴,後綴和兩端的最短路,更新類似於floyd。

SCOI

BZOJ1078-[SCOI2008]斜堆 給定一棵斜堆,要求還原插入序列。保證是一個置換。

斜堆的性質。 題解 似乎還要再想想。

BZOJ2333-[SCOI2011]棘手的操作\(n\) 個節點,每個點有一個權值,要求支持以下操作:

  • U x y 加一條邊,連接第 \(x\)個節點和第 \(y\) 個節點
  • A1 x v 將第 \(x\) 個節點的權值增加 \(v\)
  • A2 x v 將第 \(x\) 個節點所在的連通塊的所有節點的權值都增加 \(v\)
  • A3 v 將所有節點的權值都增加 \(v\)
  • F1 x 輸出第 \(x\) 個節點當前的權值
  • F2 x 輸出第 \(x\) 個節點所在的連通塊中,權值最大的節點的權值
  • F3 輸出所有節點中,權值最大的節點的權值

左偏樹,懶標記。 看到操作 U 和最值,不難想到要使用可並堆維護。由於 F3 操作的存在,我們需要維護一個 set 用於保存所有堆頂的值。而 A2 可以用線段樹的懶標記實現。A3就搞一個簡單的全局增量。A1操作需要刪除原有的節點,再加入新的。查詢的時候註意要路徑上的所有點pushdown,因為左偏樹的高度是期望 \(O(\log)\) 的,所有往上找父親的復雜度是對的。

代碼其實挺難寫的,要註意 set 修改的時候要仔細,同時A1A2操作都需要修改。

void pushdown(int x) {
    if (!h[x].lazy) return;
    if (h[x].l) { h[h[x].l].val += h[x].lazy; h[h[x].l].lazy += h[x].lazy; }
    if (h[x].r) { h[h[x].r].val += h[x].lazy; h[h[x].r].lazy += h[x].lazy; }
    h[x].lazy = 0;
}

void ALLpushdown(int x) {
    if (h[x].fa) ALLpushdown(h[x].fa);
    pushdown(x);
}

inline int getroot(int x) {
    while (h[x].fa) x = h[x].fa;
    return x;
}

int merge(int x, int y) {
    if (!x || !y) return x + y;
    pushdown(x); pushdown(y);
    if (h[x].val < h[y].val) swap(x, y);
    h[x].r = merge(h[x].r, y); h[h[x].r].fa = x;
    if (d[h[x].l] < d[h[x].r]) swap(h[x].l, h[x].r);
    d[x] = d[h[x].r] + 1;
    return x;
}

int clear(int x) {
    int t = merge(h[x].l, h[x].r), f = h[x].fa;
    h[x].fa = h[x].l = h[x].r = 0;
    if (x == h[f].l) h[f].l = t;
    else h[f].r = t;
    h[t].fa = f;
    return getroot(t);
}

int main() {
    n = read();
    rep (i, 1, n) {
        h[i].val = read();
        s.insert(h[i].val);
    }
    Q = read(); d[0] = -1;
    while (Q--) {
        op = readchar();
        if (op == ‘U‘) {
            u = getroot(read()); v = getroot(read());
            if (u != v) {
                if (merge(u, v) == u) s.erase(s.find(h[v].val));
                else s.erase(s.find(h[u].val));
            }
        }
        else if (op == ‘A‘){
            op2 = readchar();
            if (op2 == ‘1‘) {
                x = read(); y = read();
                ALLpushdown(x);
                s.erase(s.find(h[getroot(x)].val));
                h[x].val += y;
                s.insert(h[merge(x, clear(x))].val);
            }
            else if (op2 == ‘2‘) {
                x = getroot(read()); y = read();
                s.erase(s.find(h[x].val));
                s.insert(h[x].val += y);
                h[x].lazy += y;
            }
            else delta += read();
        }
        else {
            op2 = readchar();
            if (op2 == ‘1‘) {
                x = read();
                ALLpushdown(x);
                writeln(h[x].val + delta);
            }
            else if (op2 == ‘2‘) {
                x = getroot(read());
                writeln(h[x].val + delta);
            }
            else writeln(*(--s.end()) + delta);
        }
    }
    return 0;
}

BZOJ1294-[SCOI2009]圍豆豆Bean 在一個 \(n \times m\) 的矩陣中,有 \(D\) 個豆豆,都有一定的分值,還有一些障礙物。如果一個位置沒有障礙,而且沒有豆豆,就可以進入。要求從某個位置出發,最後回到這個位置,得到的價值為圍住的豆豆數量,減去走的步數。求得到的價值最大為多少。
\(n, m \leq 10, D \leq 9\)

SPFA,狀壓。 考慮枚舉每一個點作為起點,\(f[i][j][k]\) 表示到達 \((i,j)\) 可以包圍的集合為 \(k\) 的最短路徑。因為狀態存在環,跑一個SPFA就可以了。最後權值和減最短路徑就是價值。

如何判斷一個點最終是否會被圍起來?射線法! 從一點向隨機方向引一條射線,如果射線和多邊形的邊相交奇數次,說明點在多邊形內。否則在多邊形外。考慮向右連射線,這裏的相交需要左閉右開,也就是和一條豎直線段的非下端點相交才能算。

BZOJ1854-[SCOI2010]遊戲 給定 \(n\) 個數對,每個數對中最多選擇一個數字。要求選擇 \(1,2,3,...\) 能達到的最大值。

巧妙的圖論模型,並查集。 發現是一個二元關系,考慮建圖。對於一個 \(size = p\) 連通塊,如果不存在環,只能選擇 \(p-1\) 個點,存在就能選擇 \(p\) 個。顯然,可以用並查集維護連通塊是否存在環,以及連通塊的最大值即可。

BZOJ1856-[SCOI2010]字符串 求存在多少長度為 \(n+m\) 的序列,存在 \(n\)\(1\)\(m\)\(0\) ,同時任意前綴的 \(1\) 多於 \(0\)

卡特蘭數的擴展形式。終於知道卡特蘭數怎麽推了!!(如果早一點做這題,那場CF就不會血崩了QAQ)。

考慮這樣一個 \(n \times m\) 的網格,設 \(1,0\) 分別為向右上/右下走。可轉化為從 \((0,0)\) 到 $(n+m,n-m) $且不能碰到 \(y=1\) 的方案數。考慮容斥,總方案數為 \(C(n+m,n)\) 。將路線與 \(y=-1\) 第一個交點的左邊沿著 $y=-1 $ 對稱,發現不合法的方案為 \(C(n+m,m-1)\)

盜用 題解 的圖。

技術分享圖片

BZOJ1857-[SCOI2010]傳送帶 在一個 \(2\) 維平面上有兩條傳送帶,每一條傳送帶可以看成是一條線段。兩條傳送帶分別為線段 \(AB\) 和線段 \(CD\)xx\(AB\) 上的移動速度為 \(P\) ,在 \(CD\) 上的移動速度為 \(Q\) ,在平面上的移動速度 \(R\) 。現在 xx 想從 \(A\) 點走到 \(D\) 點,他想知道最少需要走多長時間。

三分套三分。 時間 顯然 是關於離開位置單峰的,三分一個 \(AB\) 再三分 \(CD\) 即可。證明嘛..

先挖坑。

JSOI

BZOJ1558-[JSOI2009]等差數列 給定 \(n\) 個數,\(m\) 個操作,每次給一段區間加一個等差數列,或者詢問一段區間至少要用多少個等差數列來表示。\(n,m \leq 10^5\)

差分,線段樹。 套路?考慮維護差分數組,修改操作變成了兩個單點加和一個區間加,用線段樹維護。

合並答案的時候比較復雜,用 \(s[0/1][0/1]\) 表示左右端點取不取的答案。一段長度為 \(n\) 等差數列就意味著,第一個是任意的差分值,剩下的 \(n-1\) 差分值相等。

BZOJ1822-[JSOI2010]Frozen Nova 冷凍波 套路的二分+最大流,需要判斷線段是否與圓相交。

計算幾何。 只是想記錄一下判斷線段 AB 是否與圓 C 相交的簡便方法。一種情況:線段的一端在圓內/上,首先直接特判掉。 第二種情況:線段兩端在圓的兩邊。

考慮與此線段平行的兩條切線中間,被圓分開的兩塊區域。線段的端點只可能分別在兩個區域。首先用叉積求出 \(2\triangle ABC\) ,然後除以長度得到與圓心的距離,大於 \(r\) 就判掉(防止卡精度,兩邊平方即可)。有可能線段的兩端都在同一個區域,必定存在一個鈍角。用點積判斷。於是,代碼:

    if (dist2(a, c) <= 1LL * r * r) return 1;
    if (dist2(b, c) <= 1LL * r * r) return 1;
    return dot(b, c, a) >= 0 && dot(a, c, b) >= 0
    && cross(c, a, b) * cross(c, a, b) <= 1LL * r * r * dist2(a, b);

NOI

BZOJ1564-[NOI2009]二叉查找樹 給定一個Treap,總代價為深度\(\times\)距離之和。可以每次以 \(K\) 的代價修改權值(權值不能相同,並且是實數),問最小代價。

區間DP。 首先有一個很坑的點,權值不能相同。 事實上,由於權值是實數,所以隨便改嘛...

然後數據範圍也是假的。隨便區間DP就好了..

POI

AUT 給定一個長度為 \(n\) 的置換。求存在多少個 \(n\) 階競賽圖滿足,如果邊 \((u,v)\) 存在,那麽 \((p(u),p(v))\) 存在。
\(n \leq 10000\)

簡單計數。 考慮將置換分解為循環。容易發現:

對於同一置換:一個長度為 \(L\) 循環的方案數為 \(2^{L/2}\)

對於不同的置換:兩個長度分別為 \(a,b\) 的循環,連接的方式有 \(2^{\gcd(a,b)}\)

TRO\(n+p+q\) 個箱子,如果填充第 \(i\) 個,那麽第 \(\{i+p,i+p+q\}\ or\ \{i+q,i+p+q\}\) 個要被填充。請構造一種方式能夠填滿前 \(n\) 個箱子。
\(n \leq 100000\)

貪心。 不妨令 \(p < q\) ,從左到右掃描,如果沒有放 \(i\)。如果可以,放 \(i+p\) 否則放 \(i+q\)

可是,為什麽這樣是滿足條件的呢?顯然,第 \(i+p+q\) 個箱子一定可以放。

不合法的情況當且僅當 \(i+p,i+q\) 都放不了。如果 \(i+q\) 放不了,那麽一定是 \(i-p\) 經過了一次操作。而如果 \(i-p\) 進行操作,並且 \(i\) 也沒放,一定會選擇放 \(\{i-p, i, i+q\}\) 這與 \(i\) 沒放矛盾。所以所有操作都能進行。

BZOJ1098-[POI2007]辦公樓biu 給定一個圖,求它反圖的連通塊數目。

鏈表優化BFS。 顯然邊數過多,不能把所有邊建出來。考慮鏈表優化。

每一次取出鏈表的第一個元素,加入隊列然後擴展。把與其相連的點標記,再遍歷鏈表,如果遇到沒有被標記的,就加入隊列。由於每個元素最多被刪除一次,每條邊最多阻止擴展2次,所以總的復雜度就是 \(O(n+m)\)

BZOJ1100-[POI2007]對稱軸osi 給定一個無自交的多邊形,求對稱軸數量。

計算幾何,KMP。 顯然,對稱軸只可能是兩個點的連線,或者是兩條線段的連線。

如何保證軸兩邊對稱?首先要確保距離相等,其次是角度(防止卡精度,又由於邊長相等,可以用點積代替)。

然後這樣就可以轉化為串s,可以倍長得到ss後,用反串t來匹配、完整的匹配次數即是答案。

BZOJ1109-[POI2007]堆積木Klo 給定一個長度為 \(n\) 的序列 \(ai\),可以刪除若幹個數。求重新標號之後,\(a_i=i\) 的個數最大值。

三維偏序。 考慮能夠滿足 \(a_i=i\) 數的要求。

首先我們要保證 \(i, a[i]\) 是遞增的,還有保證 \(i-a[i]\) 也是不減的(選擇的相鄰兩個的差不應該超過位置差)。發現這是一個三維偏序。

這是一個假的三維偏序。因為 \(a[i]\) 遞增, \(i-a[i]\) 不減時,\(i\) 一定遞增。二維偏序用 \(LIS\) 即可。

BZOJ1122-[POI2008]賬本BBB 給定一個由 \(+1\)\(-1\) 構成的長度為 \(n\) 的序列,提供兩種操作:

  1. 將某一位取反,花銷為 \(x\)
  2. 將最後一位移動到前一位,花銷為 \(y\)

要求最終 \(p+sum_n=q\),且 \(p+sum_i≥0(1≤i≤n)\),求最小花銷。

前綴和,貪心。 考慮預處理前綴,後綴最大前綴和 \(pre/suf\),枚舉起始位置。 一個很巧妙的思想就是可以根據 \(pre,suf\) 求出移動後的前綴和最小值。然後我們貪心地把前面的若幹個 \(-\) 改成 \(+\) 即可。代碼就很短了:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000009;
int sum[maxn], pre[maxn], suf[maxn];
int n, p, q, x, y, mn, cnt; char s[maxn];
long long res = 1e18, tmp;
int main() {
    scanf("%d%d%d%d%d", &n, &p, &q, &x, &y);
    scanf("%s", s+1); sum[0] = p;
    for (int i=1; i<=n; i++)
        sum[i] = sum[i-1] + (s[i] == ‘+‘ ? 1 : -1);
    suf[n+1] = suf[n] = sum[n]; pre[1] = sum[1];
    for (int i=2; i<=n; i++) pre[i] = min(pre[i-1], sum[i]);
    for (int i=n-1; i>=1; i--) suf[i] = min(suf[i+1], sum[i]);
    for (int i=2; i<=n+1; i++) {
        tmp = 1LL * (n + 1 - i) * y;
        mn = min(suf[i] - sum[i-1], pre[i-1] + sum[n] - sum[i-1]);
        if (mn < 0) { cnt = (1 - mn) / 2; tmp += 1LL * cnt * x; } else cnt = 0;
        tmp += 1LL * abs(sum[n] + cnt * 2 - q) / 2 * x;
        res = min(res, tmp);
    }
    printf("%lld\n", res);
    return 0;
}

COCI

BZOJ1182-[COCI2009]PLAHTE 在平面坐標系中,有 \(n\) 個矩形。時刻0,在原點處有一個xx,之後的每一時刻,每個xx會向周圍八個方向擴展。給定 \(Q\) 個詢問,要求回答時刻 \(t\) ,所有矩形覆蓋的xx總數是多少。註意,沒有矩形覆蓋原點。
\(n, Q \leq 10 ^ 6\)

二階導數(大霧) 二階差分,加速度。 很顯然需要搞一個前綴和。

矩形總共有兩種放置情況,一種是在一個象限,還有一種是橫跨兩個象限。為了 方便 處理,可以將後者拆分為兩個矩形,同時,我們可以將矩形強制放在第一象限。還是為了方便我們可以用容斥原理,拆分為四個左下角固定,邊長無窮大的矩形。

然後這個東西就很好弄了,擴散有一個初始速度,之後還有一個加速度。疊加起來就好了。本質上是一個二階差分。

BZOJ1939-[COCI2010] Zuma 有一行 \(n\) 個彈子,每一個都有一個顏色。每次可以讓超過 \(K\) 個的連續的同顏色的一段彈子消失,剩下的會重新緊湊在一起。你有無限的所有顏色的彈子,要求在這行彈子中插入最少的彈子,使得彈子全部消失。

真·祖瑪,動態規劃,奇妙的狀態。 定義 \(f[l][r][x]\) 表示對於 \((l,r)\) 這個子序列,我們需要插入 \(x\)\(a[l]\) 才能使原序列消失,所需要的總的最小代價。 然後有三種轉移:

直接在前面插入,\(f[l][r][x] = f[l][r][x+1] + 1\)

直接刪除,\(f[l][r][K-1] = f[l+1][r][0]\)

和後面的匹配 ,\(f[l][r][x] = f[l+1][p-1][0] + f[p][r][x+1] (l+1 \leq p \leq r, a[l] = a[p])\)

Baltic

BZOJ1356-[Baltic2009]Rectangle 給出 \(n\) 個點,要你從這些點中找出四個點來組成一個矩形,面積最大。註意可以不與坐標軸平行。

幾何基礎。 考慮一個矩形,他的兩條對角線長度相同,且中點相同。就可以 \(O(n^2)\) 計算出來,然後按照對角線長度和中點位置排序,考慮可以配對的所有對角線。因為能共圓的點不多,所以直接暴力枚舉可過。復雜度是 \(O(n^4)\) 的。

事實上不用那麽萎。考慮矩形的面積是對角線長度的平方,乘以夾角的 \(\sin\) 考慮按照極角序來排,然後用兩個指針,始終維持夾角在小於等於 \(\frac {\pi} {2}\) 的最大值。醬復雜度就是 \(O(n^2 \log n)\) 啦,可能實際並沒有快多少?

BZOJ1761-[Baltic2009]beetle 在一條直線上有 \(n\) 個點,每個點 \(m\) 升水. 一個蟲子在坐標軸 \(0\) 上,它每個單位時間移動一格,每個點的水每單位時間消失 \(1\) 升。問蟲子最多可以喝到多少水,喝水的時間忽略不計。

提前費用計算。 顯然是提前費用計算的套路,由於不知道最終要哪些點的水,枚舉喝的點的數量即可。然後就是套路區間DP啦。

APIO

BZOJ2303-[APIO2011]方格染色 有一個 \(n \times m\) 的方格,可以黑白染色,已經有某些格子已經染色。要求全部染色後,所有 \(2 \times 2\) 的區域中,恰好有奇數個黑塊。問染色的方案數。

奇妙的並查集。\(S(i,j) = a[i][j] \oplus a[i-1][j] \oplus a[i][j-1] \oplus a[i-1][j-1]\) 考慮將 \(S(1..i-1, 1..j-1)\) 全部都 xor 起來。於是 \(a[1][1] \oplus a[i][1] \oplus a[1][j] \oplus a[i][j] = [i,j都是偶數]\) 。於是就有 \(2n-1\) 個自由變元(也是因為有 \(n^2\) 個變元, \((n-1)^2\) 個方程)。

然後我們可以枚舉 \(a[1][1]\) 的值。這些方程就可以的到 \(a[i][1]\)\(a[1][j]\) 的取值是否相同。用一個帶權並查集搞一搞,判矛盾以及統計連通塊數目。

Code:

int getpa(int x) {
    if (pa[x] == x) return x;
    int p = getpa(pa[x]);
    g[x] ^= g[pa[x]];
    return pa[x] = p;
}
bool link(int x, int y, int z) {
    int u = getpa(x), v = getpa(y);
    if (u == v) return !(g[x] ^ g[y] ^ z);
    g[v] = (g[x] ^ g[y] ^ z); pa[v] = u;
    return 1;
}
int calc() {
    rep (i, 1, n+m) { pa[i] = i; g[i] = 0; }
    pa[n+1] = 1;
    rep (i, 1, Q)
        if (!link(x[i], y[i]+n, z[i])) return 0;
    int res = 0;
    rep (i, 1, n+m) if (pa[i] == i) res++;
    return Power(2, res-1);
}
int main() {
    n = read(); m = read(); Q = read();
    flag[1] = flag[0] = 1;
    rep (i, 1, Q) {
        x[i] = read(); y[i] = read(); z[i] = read();
        if (x[i] + y[i] == 2) { flag[z[i]] = 0; i--; Q--; continue; }
        z[i] ^= (x[i]^1)&(y[i]^1)&1;
    }
    if (flag[1]) ans = (ans + calc()) % MOD;
    if (flag[0]) {
        rep (i, 1, Q) if (x[i] > 1 && y[i] > 1) z[i] ^= 1;
        ans = (ans + calc()) % MOD;
    }
    printf("%d\n", ans);
    return 0;
}

BZOJ1178-[APIO2009]CONVENTION會議中心 給定若幹條線段。要求選取最多的線段,使之不重疊。在最多的基礎上,要求選取的線段字典序最小。

倍增,貪心。 如果沒有字典序最小這個條件,就是一個裸的貪心。然後這就有一個倍增的經典應用。

考慮首先搞掉所有包含其他線段的線段。然後倍增,\(f[i][j]\) 表示第 \(i\) 條線段之後,所選擇 \(2^j\) 條線段的最右邊一條。只需要貪心預處理 \(f[i][0]\)

然後仍是貪心地考慮放入線段 \(1,2,...\) ,如果放入線段之後,對答案不會產生影響,就選擇這條線段。

BZOJ1912-[APIO2010]patrol巡邏 給定一棵有根樹,可以加入 \(K = 1\ or\ 2\) 條邊,使得從 \(1\) 出發,遍歷所有的邊,問需要走的最小距離。

DFS,貪心。 對於 \(K=1\) 的情況,顯然是選取直徑的兩端,可以減小直徑-1次移動。

考慮 \(K=2\) ,假設已經增加了一條邊。對於一條邊,如果它之前已經被覆蓋了,第二次仍然被覆蓋,那麽這條邊就要經過 2 次(因為新加入的兩條邊一定要走,這條邊要走兩次才能回到原來的一側)。然後再次求最長鏈即可。

BZOJ1913-[APIO2010]signaling信號覆蓋 給定 \(n\) 個平面上的點,任意三點不共線,任意四點不共圓,問隨機選擇三個點構成的外接圓,能夠覆蓋的點數的期望值。

\(n \leq 1500\)

計算幾何。 註意到對答案的貢獻涉及到四個點的關系。所以考慮任意四個點 \(A,B,C,D\) ,分成兩種情況。構成了一個凹四邊形,那麽對答案的貢獻為 \(1\) ;構成一個凸四邊形,對答案的貢獻為 \(2\) (這個很容易通過四點共圓得到),顯然這樣的統計不會重復。

於是就變成統計凹/凸多邊形的個數。一個簡單的方法就是,枚舉凹多邊形的凹點 \(C\) ,極角排序。然後對於另一端 \(D\) ,統計 \(B,C\) 的對數。

USACO

BZOJ4757 給定長度為 \(N\) 的序列 \(ai\) ,對每個 \(ai\) 分配 \(ci\) ( \(ci\) 為正整數),且 \(ci\) 之和等於 \(K\) ,求出最小的 \(ai/ci\) 之和。

奇妙的貪心。 考慮這樣一個事實,對於 \(ai,aj\)\(ci\) 增大 \(1\) 的同時, \(cj\) 減小 \(1\) 。那麽偏移量一定是一大一小,而且增大的速度更快。根據貪心玄學的思想,我們二分偏移量,使得這個偏移量最小,那麽答案一定是最優的。

BZOJ4768 給定一個長度為 \(N\) 的序列,允許翻轉一個非連續子序列,求最長不下降子序列長度。所有數 \(<=50\)

奇妙的DP,附加狀態。\(f[i][j][l][r]\) 表示 \([i,j]\) 內左邊是 \(>=l\) ,右邊是 \(<=r\) 的最大不下降子序列長度。然後兩端的數換不換都轉移一下。

為什麽要這樣考慮呢?因為翻轉相當於 \([i,j]\) 兩邊的交換。為了保證是遞增的, \([l][r]\) 顯然是需要的。

BZOJ4741\(n\) 個點,保證不存在三點共線。對於所有構成的三角形,內部的點數就是它的價值。對於每一價值,計算三角形的塊數。
\(n \leq 300\)

奇妙的容斥原理,計算幾何。 似乎很不可做的樣子,事實上,利用容斥原理,我們就可以做到 \(O(1)\) 計算一個三角形內的點數。考慮預處理:

對於所有二元組 \((u,v)\) ,計算滿足向量 \((u,v) \rightarrow (u,p)\) 的角不大於 \(\pi\) 的點 \(p\) 數論。

對於所有三元組 \((u,v,w)\) ,計算滿足 \(p\) 在向量 \((u,v) \rightarrow (u,w)\) 之間的個數。

之後配一波系數,容斥一下就好了。

BZOJ4586 給定一個長度為 \(n\) 的初始序列 \(Ai\) ,和一個目標序列 \(Bi\) 。可以花費 \(x\) 的代價使得一個數增加 \(1\) ,或者花費 \(y\) 的代價使得一個數減少 \(1\) 。對於 \((i,j)\) 花費 \(|i-j|z\) 的代價使得 \(Ai\) 減一, \(Aj\) 加一。問最小需要的代價總和。
\(n \leq 100000\)

奇妙的貪心,反悔操作。 考慮經典的堆來維護返回操作。維護兩個堆,分別表示多余的和缺少的。對於每個樹默認操作是直接增加或減少,同時把這個反悔後的新代價放入堆。

BZOJ4410 給定一個網格圖,拆除所有行的豎向邊,拆出所有列的橫向邊的代價都已經給定。問使得所有方格連通的最小代價。

堆,Kruskal。 考慮Kruskal算法的思想,我們要求最小生成樹,就需要按照邊權從小到大選擇。拆出未連接的邊即可。註意一個方向的所有邊都拆完不一定連通。

BZOJ4411 給定 \(n\) 個點,請你確定一條橫線和一條豎線,使得平面劃分成的四個區域內點最大值最小.

線段樹上二分。 枚舉直線 \(x=a\) ,顯然兩邊在線段樹中可以動態插入/刪除。二分 \(y=b\) 的位置。註意到兩端是一個下凸函數,取 max 之後仍然下凸。這個東西就可以線段樹二分辣。

BZOJ4409 有一個 \(N\) 個點的環,最終點 \(i\) 存在 \(ri\) 頭牛。有 \(\sum ri\)頭牛。可以選擇最多 \(k\) 個點,然後牛分配在這k個點裏。之後每一頭牛可以不動,也可以順時針走 \(d\) 格並呆在那裏,要耗費d的能量。通過合理分配。使得消耗的總能量最小。
\(n \leq 1000\)

斜率優化DP。 由於存在環,顯然我們可以枚舉第一個選擇點的位置 \(i\) 。用 \(f[j][k]\) 表示從 \(i\) 出發,選擇 \(k\) 個點,到 \(j\) 的最優解。然後這個東西是可以斜率優化的。

同時有註意到切線是遞增的,所以可以做到 \(O(n^2k)\) 。然鵝這個東西是要用 \(k-1\) 層去更新第 \(k\) 層。所以需要把 \(k\) 放在外面。

隨便一寫就rk3了??

BZOJ3939 有一個 \(n \times m\) 的網格,每個格子有一個取值。從 \((1,1)\) 出發,每一次可以跳到嚴格右下方的格子。問存在多少中方案能夠跳到 \((n,m)\)
\(n, m \leq 750\)

動態規劃,動態開點線段樹。 顯然DP+容斥嘛。維護一個二維前綴和,減去與當前顏色相同的方案數。

考慮對每個顏色開一個動態開點線段樹。每一次最多增加 \(O(\log n)\) 個節點。所以總共的空間是 \(O(n^2 \log n)\) 的。

BZOJ4509 數軸上有 \(n\) 個點。可以在任意一個位置上引爆,使得爆炸範圍內的點再次引爆,但是引爆半徑減一。問最小的初始引爆半徑,使得所有點都被引爆。

動態規劃,單調性。 註意到左右兩部分是無關的,所以我們可以求出前綴和後綴所需要的最小引爆半徑。

\(f[i]\) 表示 \(i\) 左邊都被引爆的最小半徑。於是有:

\(f[i] = \min \{ ai - aj, f[j+1] + 1 | j < i, a[i] - a[j] > f[j] \}\)

註意到前半部分是隨著 \(j\) 的變大而單調遞減的,而後面是單調遞增的。所以我們可以求出兩個接近時候的最值。復雜度 \(O(n)\)

BZOJ4099 數軸上有 \(n\) 個點,每個點有一個大小 \(si\)。從任意一個位置出發,沿著同一方向跑至少 \(si\),就可以突破點 \(i\),得到更大的空間。問無法逃脫的線段總長度。

set 考慮按照點的大小,從大到小插入。當插入一個點後,查找它的前驅和後繼,如果無法突破,就打上標記。由於每個點至多被標記一次,所以復雜度是對的。

BZOJ3429 有一個 \(n \times m\) 的01矩陣,以及他的目標狀態。每一次可以修改 \(b*b\) 大小的子矩陣,使得原來的矩陣到達目標狀態。問最大的 \(b\)是多少。

模擬? 顯然,這個 \(b\) 是滿足單調性的。當大的 \(b\) 滿足時,小的 \(b\) 也滿足。所以考慮暴力修改,當無法再次修改時,用更小的 \(b\) 替換。

Other

BZOJ1426-收集郵票 \(n\) 種郵票,你要收集所有種類的郵票。每次能買一張,買到的郵票是哪一種,概率均為 \(1/n\) 。購買第 \(k\) 張郵票需要 \(k\) 元錢。現在手中沒有郵票,問得到所有種類的郵票需要錢的期望。

期望神題,平方相關。 先挖坑吧。。智商還不夠

題解一

題解二

BZOJ2708-[Violet 1]木偶 給定兩個序列 \(\{a_i\},\{b_i\}\) ,滿足 \(a_i = b_i\) 。重復一下步驟,選擇任兩個 \(i,j\) 滿足 \(|a_i-b_j| \leq 1\) ,將個兩個數配對,直到無法再配對。問最壞情況下,未配對的 \(a_i\) 最多有多少個。

貪心,動態規劃。 顯然可以從小到大排序。這就是一個二分圖,一個顯然的性質:邊不會出現交叉。因為只能連接差的絕對值不超過 \(1\) 的數,所以交叉只會是相鄰的兩個,顯然這個可以用連向自己的來代替,並且答案不會更劣。

然後就可以推出結論,配對一定是形如 //..//\\..\\//... 這樣延伸下去的。然後就可以DP了。需要搞一個 \(calc(i,j)\) 表示 \([i,j]\) 無法配對的最多有多少。考慮從大到小枚舉,判斷 應該/不應該 配對的兩個數能否配對。

「泛做題解」曾經的 BZOJ 題解匯總