1. 程式人生 > 其它 >[噼昂!]problem(Pending)

[噼昂!]problem(Pending)

\[\color{red}{\text{校長者,真神人也,左馬桶,右永神,會執利筆破邪炁,何人當之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本當に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{對曰:“無人,狗欲當之,還請賜教!”}} \\ \newcommand\bra[1]{\left({#1}\right)} \newcommand\Bra[1]{\left\{{#1}\right\}} \newcommand\dx[0]{\text{dx}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \]
目錄

壹、關於題目 ¶

還是沒有時間去編......

貳、關於題解 ¶

哦?這道題 \(n\) 居然只有 \(10^5\),那不是 \(\mathcal O(n^2m)=\mathcal O(n^4)\) 隨便跑啊。

這固然是一種美妙的解法,不過仍然有優化的空間。


◆ 結論 の 開端

其實這道題為經典的 \(\rm Gale-Ryser\) 型刻劃定理模型:

\(P_m = p_1,p_2,\cdots,p_m\)\(Q_n=q_1,q_2,\cdots,q_n\)

是兩個由非負整數構成的不增序列。如果存在一個簡單 \(X,Y\) 二部圖使得 \(X\) 中的頂點的度分別為 \(p_1,p_2,\cdots,p_m\)\(Y\) 中的頂點的度分別為 \(q_1,q_2,\cdots,q_n\),那麼稱序列對 \((P_m,Q_n)\) 是二部可圖的。

\((P_m,Q_n)\) 是二部可圖的充要條件為

\[\forall k\in [1,m],\sum_{i=1}^k p_i\le \sum_{i=1}^n\min(q_i,k) \;\text{holds} \]

維基百科傳送門.

知道這個,我們就可以做許多事情。


◆ 結論 の 使用

將結論轉到這道題上面,即先將 \(a\)

從大到小排序,然後

\[\forall k\in [1,n],\sum_{i=1}^k a_i\le \sum _{i=1}^m \min(b_i,k)\;\text{holds} \]

趕腳右邊的 \(\min (b_i,k)\) 很難搞定,我們可以做一個轉換:記 \(c_k=\sum [b_i\ge k]\),那麼右邊

\[\sum_{i=1}^m\min(b_i,k) = \sum_{i=1}^k c_k \]

感覺上來說挺直觀的,不解釋了。

那麼,不等式的成立就是

\[\forall k\in [1,n],\sum_{i=1}^kc_i-a_i\ge 0 \;\text{holds} \]

於是乎,使用線段樹維護所有 \(k\)

對應的值得最小值就可以解決這個問題。

比較細節的一個地方是修改 \(a_i\) 的時候,由於我們不想改變其不降的規則,我們可以選擇每次在相同值平臺的左右端點改,這樣就不會破壞 \(a\) 不降了。

時間複雜度 \(\mathcal O(n\log n)\).


◆ 結論 の 終焉

找了很多資料,沒什麼比較好的證明,維基百科使用矩陣,然鑑於我是線代戰爭的炮灰,便不了了之了。後來發現就從網路流方向入手,反而可能是更容易理解的。

先將 \(a_i\) 由大到小排序,不妨建立這樣一個網路:從源點向每個左部點連邊,其容量為 \(a_i\),從每個右部點向匯點連邊,容量為 \(b_i\),每個左右部點之間都有一條容量為 \(1\) 的邊。下設 \(S,T\) 分別表示左右部點的集合,\(x,y\) 分別為源匯點。

假設某種割邊方案,\(x\to a_p(p\in [1,k])\) 的邊都未被割掉,而 \(x\to a_q(q\in [k+1,n])\) 的邊都被割掉,我們再設 \([S],[T]\) 表示最終割完,與 \(x\) 同一集合的左部點點集為 \([S]\),右部點點集為 \([T]\),那麼該最小割方案的花費可以表示為

\[\sum_{i\notin [S]}a_i+\sum_{i\in [T]}b_i+|[S]|(m-|[T]|) \]

根據我們的前置條件,我們可以化簡式子:

\[\begin{aligned} (*)&=\sum_{i=k+1}^n a_i+\sum_{i\in [T]} b_i+k(m-|[T]|) \\ &=\sum_{i=k+1}^na_i+\sum_{i\in [T]}b_i+\sum_{i\notin [T]}k \end{aligned} \]

後面兩項可以看成是,將某些 \(b_i\) 換成了 \(k\). 由於我們想要最小割,所以我們肯定將那些大於 \(k\) 的換掉,所以,存在不等式

\[(*)\ge \sum_{i=k+1}^n a_i+\sum_{i=1}^m \min(b_i,k) \]

由於 \([T]\) 是我們自己設定的,所以我們可以構造 \([T]\) 使得 \(b_i\ge k\) 的點都不在 \([T]\) 中,便達到了下界,同時,最小割有

\[mincut=\min_{k=1}^n\set{\sum_{i=k+1}^n a_i+\sum_{i=1}^m \min(b_i,k)} \]

顯然,原圖是二部可圖,當且僅當

\[maxflow=mincut\ge\sum_{i=1}^n a_i \]

這就是說,對於任意 \(k\in[1,n]\),都有這個不等式成立,即

\[\begin{aligned} \forall& k\in [1,n],\sum_{i=k+1}^n a_i+\sum_{i=1}^m \min(b_i,k)\ge \sum_{i=1}^n a_i\;\text{holds} \\ \Rightarrow \forall& k\in [1,n],\sum_{i=1}^k a_i\le \sum _{i=1}^m \min(b_i,k)&\square \end{aligned} \]

叄、關於標程 ¶

#include <bits/stdc++.h>
using namespace std;
 
// # define USING_STDIN
// # define NDEBUG
// # define NCHECK
#include <cassert>
 
namespace Elaina {

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
 
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair <int, int> pii;
    typedef pair <ll, ll> pll;
    
    template <class T> inline T fab(T x) { return x < 0 ? -x : x; }
    template <class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
    template <class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
 
#ifndef USING_STDIN
    inline char freaGET() {
# define BUFFERSIZE 1 << 17
        static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
        return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
    }
# define CHARGET freaGET()
#else
# define CHARGET getchar()
#endif
    template <class T> inline T readret(T x) {
        x=0; int f = 0; char c;
        while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1;
        for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
        return f ? -x : x;
    }
    template <class T> inline void readin(T& x) { x = readret(T(1)); }
    template <class T, class... Args> inline void readin(T& x, Args&... args) {
        readin(x), readin(args...);
    }

    template <class T> inline void writc(T x, char s='\n') {
        static int fwri_sta[55], fwri_ed = 0;
        if(x < 0) putchar('-'), x = -x;
        do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
        while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
        putchar(s);
    }

} using namespace Elaina;

const int maxn = 250000;

int n, m, q;
int a[maxn + 5], b[maxn + 5];

inline void input() {
    readin(n, m);
    rep(i, 1, n) readin(a[i]);
    rep(i, 1, m) readin(b[i]);
}

int cnt[maxn + 5], tmp[maxn + 5], revTmp[maxn + 5];
ll preCnt[maxn + 5], preTmp[maxn + 5];
namespace saya {
    
    ll mn[maxn << 2 ^ 3], tag[maxn << 2 ^ 3];

#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid + 1, r

    inline void pushup(int i) { mn[i] = min(mn[ls], mn[rs]); }
    inline void add(int i, ll v) { mn[i] += v, tag[i] += v; }
    inline void pushdown(int i) {
        if(!tag[i]) return;
        add(ls, tag[i]), add(rs, tag[i]), tag[i] = 0;
    }
    void build(int i = 1, int l = 1, int r = n) {
        tag[i] = 0;
        if(l == r) return mn[i] = preCnt[l] - preTmp[l], void();
        build(_lhs), build(_rhs), pushup(i);
    }
    void modify(int ql, int qr, ll v, int i = 1, int l = 1, int r = n) {
        if(ql > qr || qr < l || r < ql) return;
        if(ql <= l && r <= qr) return add(i, v);
        pushdown(i);
        if(ql <= mid) modify(ql, qr, v, _lhs);
        if(mid < qr) modify(ql, qr, v, _rhs);
        pushup(i);
    }

#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs

} // using namespace saya;
inline void prelude() {
    rep(i, 1, m) ++cnt[b[i]];
    // lawks! upper limit should be @p maxn
    for(int i = maxn; i; --i) cnt[i] += cnt[i + 1];
    memcpy(tmp + 1, a + 1, n << 2);
    sort(tmp + 1, tmp + n + 1, [](const int x, const int y) { return x > y; });
    memcpy(revTmp + 1, tmp + 1, n << 2);
    reverse(revTmp + 1, revTmp + n + 1);
    for(int i = 1; i <= n; ++i) {
        preCnt[i] = preCnt[i - 1] + cnt[i];
        preTmp[i] = preTmp[i - 1] + tmp[i];
    }
    saya::build();
}

inline void work() {
    int q, op, id, pos;
    readin(q);
    while(q--) {
        readin(op, id);
        if(op == 2) {
            pos = lower_bound(revTmp + 1, revTmp + n + 1, a[id]) - revTmp;
            --revTmp[pos], --a[id];
            saya::modify(n - pos + 1, n, 1);
        }
        else if(op == 1) {
            pos = upper_bound(revTmp + 1, revTmp + n + 1, a[id]) - revTmp - 1;
            ++revTmp[pos], ++a[id];
            saya::modify(n - pos + 1, n, -1);
        }
        else if(op == 4) saya::modify(b[id], n, -1), --b[id];
        else ++b[id], saya::modify(b[id], n, 1);
        writc((int)(saya::mn[1] >= 0));
    }
}

signed main() {
    // freopen("problem.in", "r", stdin);
    // freopen("problem.out", "w", stdout);
    input();
    prelude();
    work();
    return 0;
}

肆、關鍵 の 地方 ¶

就是 \(\rm Gale-Ryser\) 定理咯~~~~