AtCoder瞎作第二彈

ARC 067

F - Yakiniku Restaurants

題意

\(n\) 家飯店,\(m\) 張餐票,第 \(i\) 家和第 \(i+1\) 家飯店之間的距離是 \(A_i\) ,在第 \(i\) 家飯店用掉第 \(j\) 張餐票會得到 \(B_{i, j}\) 的好感度,能夠從任意一個飯店出發,求好感度減通過的距離和的差的最大值。c++

\(2 \le n \le 5000, 1 \le m \le 200, 1 \le A_{i, j}, B_{i, j} \le 10^9\)git

題解

作題千萬條,看題第一條。app

顯然咱們不會走回頭路,那麼每次咱們選擇的必定是一段連續區間 \([l, r]\)函數

考慮每一個 \(B_{i, j}\) 對於哪些區間有貢獻,找到左右第一個比它的 \(B_{x, j}, B_{y, j} \ge B_{i, j}\) ,那麼它貢獻的區間其實就是 \(l \in (x, i], r \in [i, y)\)優化

咱們利用單調棧算出端點,而後矩形加利用二維差分實現便可。spa

\(\mathcal O(n^2 + nm)\)debug

代碼

Submission #5013272rest

ARC 068

F - Solitaire

題意

有一個雙端隊列。code

首先將 \(n\) 個數 \(1\sim n\) 從小到大任意先後地添入隊列。而後任意先後地彈出隊列,求最後彈出來的排列中,第 \(k\) 個數爲 \(1\) 的排列有多少種。

\(1 \le k \le n \le 2000\)

題解

一開始添完的序列性質顯然是將 \(1\) 分紅兩段,左邊遞減,右邊遞增。

因爲構造合法序列是左右彈元素,那麼性質就比較好推了。

  • \(k\) 個數爲 \(1\)
  • \(k - 1\) 個數可拆分爲至多兩個降低子序列;
  • \(k - 1\) 個數的最小值必定大於後 \(n - k\) 個數的最大值。

先考慮最後 \(n - k\) 個數的方案,若是咱們肯定了前 \(k\) 個數,那麼剩下的 \(n - k\) 個數是由一個單調隊列彈出來的,除了最後一次只能彈一個,別的每次都有兩種選擇,因此方案是 \(2^{\max(0, n - k - 1)}\)

而後前面拆分紅至多兩個降低子序列,這個和 這道題 是同樣的。

咱們如今只須要知足第一個限制了,因爲第 \(k\) 個數是須要最小值,用至多選 \(k\) 個和 \(k - 1\) 個差分一下便可。

而後利用以前那個題的組合數結論就能夠作到 \(\mathcal O(n)\) 了。

其實那個組合數有個更優美的形式,也就是 \(\displaystyle {n + m \choose m} - {n + m \choose m - 1}\) ,意義明顯許多。

代碼

Submission #5016238

ARC 070

E - Narrow Rectangles

題意

\(n\) 個高爲 \(1\) 的矩形,第 \(i\) 個矩形 \(y\) 軸範圍爲 \([i - 1, i]\)\(x\) 軸範圍爲 \([l_i, r_i]\)

須要橫向移動一些矩形,使得全部矩形是連通的(角也算),對於一個矩形,橫向移動 \(x\) 距離的代價爲 \(x\) ,求出最小代價。

\(1 \le n \le 10^5, 1 \le l \le r \le 10^9\)

題解

首先考慮暴力 \(dp\) 即令 \(f_{i, j}\) 爲第 \(i\) 層矩形右端點爲 \(j\) ,前 \(i\) 層已經聯通所須要的最小代價。

\(len_i = r_i - l_i\) ,每次只須要兩條線段相交便可。轉移十分顯然:
\[ \begin{aligned} f_{i, p} &= |r_i - p| + \min_{p - len_i \le j, p \ge j - len_{i - 1}} f_{i - 1, j} \\ &= |r_i - p| + \min_{j - len_{i - 1} \le p \le j + len_i} f_{i - 1, j} \end{aligned} \]

咱們能夠把 \(f_i\) 看作一個關於 \(p\) 的函數,設 \(g_i(p) = |r_i - p|\) ,那麼形式爲:
\[ f_i(p) = g_i(p) + \min_{j - len_{i - 1} \le p \le j + len_i} f_{i - 1}(j) \]

不難觀察到函數這個圖像實際上是一個分段一次函數,且斜率從 \(-(i - 1), - (i - 2), \cdots, 0, \cdots, i - 2, i - 1\) 遞增(拆開重合點)。(不難利用概括證實)其實也是一個凹函數,最小值在 \(k = 0\) 處取得。

那麼考慮後面那個 \(\min\) 等價於把 \(k_x < 0\) 的部分向左平移 \(len_{i - 1}\) (由於咱們想盡可能向右取),\(k_x > 0\) 的部分向右平移 \(len_i\) ,而後最後全局加上 \(g_i\) 就好了。

咱們其實能夠用 \(Splay\) 維護這個凸殼,每次只須要支持區間加一次函數,全局平移便可。

但顯然能夠更方便地解決,因爲最後咱們只須要求 \(k = 0\) 時候的函數值,咱們利用對頂堆維護 \(k < 0, k > 0\) 的位置,每次討論一下插入的絕對值函數的 \(0\) 點位置便可。

討論的時候能夠順便計算一下當前的答案。

總結

對於加絕對值函數,而且取 \(\min, \max\)\(dp\) 均可以考慮是否存在凸殼,而後經過 線段樹/ \(Splay\) / 對頂堆 維護這個 \(dp\) 值便可。

代碼

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

typedef long long ll;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("E.in", "r", stdin);
    freopen ("E.out", "w", stdout);
#endif
}

const int N = 1e5 + 1e3;

int n, l[N], r[N], len[N];

ll tl, tr, ans;

priority_queue<ll> L;
priority_queue<ll, vector<ll>, greater<ll>> R;

int main () {

    File();

    For (i, 1, n = read()) 
        l[i] = read(), r[i] = read(), len[i] = r[i] - l[i];

    L.push(r[1]); R.push(r[1]);
    For (i, 2, n) {
        tl -= len[i - 1]; tr += len[i];
        ll lp = L.top() + tl, rp = R.top() + tr;
        if (lp <= r[i] && r[i] <= rp) 
            L.push(r[i] - tl), R.push(r[i] - tr);
        else if (r[i] >= rp) {
            ans += r[i] - rp; R.pop(); L.push(rp - tl);
            R.push(r[i] - tr); R.push(r[i] - tr);
        } else {
            ans += lp - r[i]; L.pop(); R.push(lp - tr);
            L.push(r[i] - tl); L.push(r[i] - tl);
        }
    }
    printf ("%lld\n", ans);

    return 0;

}

F - HonestOrUnkind

題意

\(n = a + b\) 我的,其中有 \(a\) 我的是誠實的,\(b\) 我的是不友好的。每次你能夠問 \(x\)\(y\) 是否是一個誠實的人。若是 \(x\) 是誠實的,那麼必定會回答真正的答案。不然,他會隨便告訴你一個結果。(交互庫有必定策略地回答。)

如今告訴你 \(a, b\) ,你須要肯定是否必定能問出來。若是問不出來輸出 Impossible 。若是能問出來,須要在 \(2n\) 步內問出來。

題解

首先咱們考慮何時是 Impossible ,顯然當 \(b \ge a\) 的時候,\(b\) 能夠很好的隱藏在 \(a\) 中。由於問任意一我的,\(b\) 均可以根據 \(a\) 的決策,來顛倒黑白。只有當 \(a\) 超過 \(n\) 的一半的時候,咱們問任意一我的均可以根據 \(\text{Y, N}\) 中較多的那項肯定類別。

接下來,咱們不難想到一個亂搞。就是一開始隨機問幾我的,而後問全部人他的類別,就能夠肯定類別了。若是是老實人,而後就能夠一遍問它就能獲得全部人的類別了。咱們打亂一下詢問順序,那麼這樣指望下是 \(3n\) 的。

咱們其實能夠繼續優化一下亂搞,加上以下幾個優化:

  • 若是問出當前人的類別,以前回答類別不一樣的人,確定不是老實人,以後咱們全都跳過不問便可。
  • 若是咱們當前問的 \(\text{Y, N}\) 其中較多的那個個數,大於剩下沒有肯定的不友好的人數,就能夠肯定這我的的類別了。
  • 若是當前只剩下友好/不友好,咱們就能夠直接不問,而後肯定便可。

指望應該是 \(1.5n\) 的?而後全都加上。。就能夠過啦qwq(我也是交了十幾發才艹過的。。)


顯然有肯定性作法,咱們須要基於這樣一個結論,若是 \(x\)\(y\) 是不友好的,那麼 \(x, y\) 確定不可能同時是誠實的,若是咱們忽略他們,剩下的老實人個數仍然大於一半。

咱們用個棧,把每一個人放進去,若是棧頂這我的說當前這我的是不友好的,咱們把棧頂彈出,而後忽略他們。

而後最後剩下了 \(a_0, \cdots, a_{k - 1}\) 其中每一個 \(a_i\) 都說 \(a_{i + 1}\) 是誠實的,那麼顯然 \(a_{k - 1}\) 必定是誠實的。爲何呢?由於其中必定有我的是老實人,那麼在它後面的全部人必定都是老實人,那麼最後一我的必是老實人。

而後咱們就能夠在穩定的 \(2n\) 次裏問出全部人的類別啦。(好妙啊~)

代碼

放個瞎JB亂搞

ARC 072

F - Dam

題意

有一個容量爲 \(L\) 的水庫,天天晚上能夠聽任意體積的水。天天早上會有必定溫度和體積的水流入水庫,且要保證流入水以後水的整體積不能超過 \(L\) 。令體積分別爲 \(V_1,V_2\) ,溫度分別爲 \(t_1,t_2\) 的水混合後的溫度爲 \(\displaystyle \frac {V_1 * t_1 + V_2 * t_2} {V_1 + V_2}\) 。初始水庫爲空。現給出 \(n\) 天流入水的體積和溫度,分別最大化每一天中午水庫滿容量時的水溫。

\(1 \le n \le 5 \times 10^5\)

題解

一道頗有意思的題~

咱們能夠發現兩個性質:

  1. 當前水溫小於之前水溫時必然會拉低總水溫,因此必定會和前面的水混合,直接向前不斷合併便可。
  2. 當前水溫大於前面時,直接將前面捨棄能夠獲得更高的溫度,但要求總量必須爲 \(L\) ,這樣有可能出現不夠加滿水壩的狀況,所以還要保留一段。

咱們利用一個單調隊列(隊列中的元素隊首到隊尾按 \(t\) 單調遞增),每次當前體積 \(>L\) 咱們不斷彈掉隊首,使得體積變小。而後隊尾溫度比隊尾前一個低,咱們就合併,直至不能合併便可。

至於爲何是對的呢?你能夠考慮把每一個水看作一個向量,咱們至關於看向量和的斜率,咱們其實就是須要貪心地維護一個下凸殼,本質上是同樣的。

代碼

Submission #5206287

ARC 073

E - Ball Coloring

題意

\(n\) 個盒子,每一個盒子裏面有兩個球,分別寫了一個數字 \(x_i, y_i\) 。如今須要把每一個盒子其中的一個球染成紅色,另一個染成藍色。

\(R_{\max}\) 爲紅球數字最大值,其餘的同理,求 \((R_{\max} - R_{\min})(B_{\max} - B_{\min})\) 的最小值。

\(n \le 2 \times 10^5\)

題解

腦子是個好東西,我也想要一個QAQ

令全局 \(x_i, y_i\) 最大值爲 \(A_{\max}\) ,最小值爲 \(A_{\min}\) 。顯然 \(R_{\max}, B_\max\) 其中一個須要取到 \(A_\max\)\(\min\) 同理。

咱們考慮分兩種狀況討論。

  • 最大值和最小值被兩邊分別取到了。

    不妨令 \(R_\max = A_\max, B_\min = A_\min\) ,那麼咱們顯然須要最小化 \(B_\max\) ,最大化 \(R_\min\)

    那麼顯然對於每一個盒子,咱們把較小的那個給 \(B\) ,較大的給 \(R\) ,顯然是最優的。

  • 最大值和最小值都給一邊取到了。

    不妨令 \(R_\max = A_\max, R_\min = A_\min\) ,那麼咱們就須要最小化 \(B_\max - B_\min\)

    咱們考慮從小到大枚舉 \(B_\min\) ,而後動態維護 \(B_\max\) 的最小值。

    若是當且 \(B_\min = A_\min\) ,咱們顯然 \(B_\max\) 取到全部的 \(\min\{x_i, y_i\}\) 的最大值是最優的。

    而後咱們每次把 \(B_\min\) 變大,也就是翻轉 \(B_\min\) 的顏色,隨便維護一下最值便可。

\(\mathcal O(n \log n)\)

代碼

Submission #5212035

F - Many Moves

題意

一個長爲 \(n\) 的數軸,一開始上面有兩個盒子在 \(A, B\) ,有 \(q\) 次要求,每次給出一個座標 \(x_i\) ,須要把其中一個盒子移到 \(x_i\) ,問最少移動的距離和。

\(1 \le n, q \le 2 \times 10^5\)

題解

惟一一道本身作出來的 \(F\) TAT 雖然很水。

假設當前處理第 \(p\) 個要求。考慮 \(dp\) ,設 \(f_{i, j}\) 爲當前兩個盒子分別在 \(i, j\) 的最少距離和,轉移顯然。

但顯然每次咱們有個盒子的位置必定在 \(x_{p - 1}\) ,咱們只須要記 \(f_i\) 爲其中一個盒子在 \(x_{p - 1}\) ,另一個在 \(i\) 的最少距離和。

顯然一次咱們不會同時移動兩個盒子,這樣必定不優。

  • \(i \not = x_{p - 1}\) ,顯然不動 \(i\) ,動 \(x_{p - 1}\) 便可,因此有 \(f'_i = f_i + |x_p - x_{p - 1}|\)
  • 對於 \(i = x_{p - 1}\) 咱們考慮枚舉上次的另一個位置 \(j\) ,那麼有 \(f_{x_{p - 1}} = \min_{j} \{f_j + |x_p - j|\}\)

直接實現是 \(\mathcal O(n^2)\) 的,對於第一個轉移就是全局加,對於第二個轉移拆絕對值,而後維護 \(f_i \pm i\) 的最小值便可。

均可以用線段樹實現 \(\mathcal O(n \log n)\)

代碼

Submission 5230528

ARC 075

F - Mirrored

題意

定義 $rev(n) $ 爲將 \(n\) 的十進制位翻轉的結果,例如 \(rev(123) = 321, rev(4000) = 4\)

給定正整數 \(D\) ,求有多少個 \(N\) 知足 \(rev(N) = N + D\)

\(1 \le D < 10^9\)

題解

考慮固定長度爲 \(L + 1\) ,假設從低到高每一位分別是 \(b_i\) ,那麼其實就是
\[ \begin{aligned} rev(N ) - N &= \sum_{i = 0}^{L} (10^{L - i} - 10^i) b_i\\ &= \sum_{i = 0}^{\lfloor \frac L2\rfloor} (10^{L - i} - 10^i) (b_i - b_{L - i}) \end{aligned} \]

咱們等價求把 \(0 \sim \lfloor \frac L2\rfloor\) 的每一個 \(v_i = 10^{L - i} - 10^i\) 乘上 \(-9 \sim 9\) 和爲 \(D\) 的方案數。(對於每一個 \(-9 \sim 9\) 對應了兩個數的一種組合)。

直接 \(dfs\) 可能會 TLE ,考慮利用性質優化,咱們觀察到:
\[ v_i > \sum_{j = i + 1}^{\lfloor \frac L2\rfloor} 9 v_j \]

那麼意味着若是咱們肯定前 \(i\) 位,組合出來的數與 \(D\) 相差 \(v_i\) 時,顯然是之後不管如何也沒法恢復的。

那麼每一步其實咱們的填法從 \(18\) 下降到只有 \(2\) 種了。

咱們須要枚舉的長度應該是 \(L_D \sim 2L_D\)\(L_D\)\(D\) 十進制的長度),由於咱們加減的範圍應該是恰好 \(L_D\) 的,超過 \(2L_D\) 加減最小數已經超過了 \(D\) 顯然沒法獲得。

有科學的複雜度爲
\[ \begin{aligned} \mathcal O(\sum_{i = L_D}^{2L_D} 2^{\lfloor \frac i 2\rfloor}) &= \mathcal O(2^{L_D}) = \mathcal O(2^{\log_{10} D})\\ &= \mathcal O(2^{\frac{\log_2 D}{\log_2{10}}}) = \mathcal O(D^{\log_{10} 2}) \approx \mathcal O(D^{0.3010}) \end{aligned} \]

跑的挺快的。

代碼

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

typedef long long ll;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
    int x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("F.in", "r", stdin);
    freopen ("F.out", "w", stdout);
#endif
}

int D, len, up;

ll Pow[20], v[20];

int Pool[20], *d = Pool + 10;

ll Dfs(ll cur, int dep) {
    if (dep == up) return !cur;
    int t = cur / v[dep]; ll res = 0;
    For (i, t - 1, t + 1) if (abs(i) <= 9 && abs(cur - i * v[dep]) < v[dep])
        res += (d[i] - (i >= 0 && !dep)) * Dfs(cur - i * v[dep], dep + 1);
    return res;
}

int main () {

    File();

    for (int tmp = (D = read()); tmp; tmp /= 10) ++ len;

    Pow[0] = 1;
    For (i, 1, 18) 
        Pow[i] = 10ll * Pow[i - 1];

    Rep (i, 10) Rep (j, 10) ++ d[i - j];

    ll ans = 0;
    For (i, len, len << 1) {
        For (j, 0, up = i >> 1)
            v[j] = Pow[i - j - 1] - Pow[j];
        ans += (i & 1 ? d[0] : 1) * Dfs(D, 0);
    }
    printf ("%lld\n", ans);

    return 0;

}

ARC 079

F - Namori Grundy

### 題意

給你一個 \(n\) 個點的有向環套樹,須要對於每一個點定取值 \(a_i \ge 0\) ,知足。

  • 對於全部邊 \((i, j)\)\(a_i \not = a_j\)
  • 對於 \(0 \le x < a_i\) 都存在至少一條邊 \((i, j)\) 使得 \(a_j = x\)

問是否存在一種合法方案。

\(2 \le n \le 2 \times 10^5\)

題解

其實操做本質上其實就是個 \(\mathrm{mex}\) ,對於樹上的 \(\mathrm{mex}\) ,每一個節點的值應該是肯定的。

須要考慮環,處理完全部環上節點的子樹,就得出了每一個環上節點的取值下界 \(b_i\)

  1. 全部 \(b_i\) 相同且環長度爲偶數:咱們隔一個數就把當前數加 \(1\) 便可。

  2. 全部 \(b_i\) 相同且環長度爲奇數:那麼隔一加一的就不行了,其實不難發現這樣沒法構造出合法方案。

  3. 存在有 \(b_i\) 不一樣:找到 \(b_i\) 最小的點 \(v\) 把它 \(+1\) ,而後依次考慮 \(u \to v\) 而後若是 \(b_u = b_v + 1\) 那麼咱們繼續操做便可,不難發現這樣操做必定會在某點中止不會繞圈。

這樣咱們就在 \(\mathcal O(n)\) 的時間內判斷出來了。

代碼

Submissions 5475398

相關文章
相關標籤/搜索