2019 ICPC 上海 M Blood Pressure Game [血壓遊戲] 出題人原封不動標程

忘記貼個中文題面了~~

作爲前來上海大學參加ICPC比賽的退役ACMer現役JBer,小明每次來上海大學參加比賽,都會打鐵。心裏就會產生一種"這是什麼JB比賽"的念頭。爲了排解痛苦,他就會去上海的迪士尼坐過山車。他很喜歡這種血壓拉滿的感覺,讓他感覺自己如同變成了一隻"快樂"的Flappy Bird,忘記了所有的WA和TLE……
過山車有一系列的高低起伏的轉折點,各個轉折點的高度,按照路徑順序形成了一個數列a[],小明坐完所有過山車後,他的血壓可以認爲是Σ(|a[i + 1] - a[i]|),即相鄰過山車高度的絕對值之和。
小明比賽一直打鐵,一直打鐵,於是一直需要不停地坐過山車,然而,可以使得他忘記煩惱的所需血壓閾值也在慢慢上升。漸漸地,上海迪士尼的過山車,已經達不到小明對自己血壓的要求了。
因此,小明開始設想增加額外的m個轉折點b[],以任意的位置和順序(此處需要討論具體規則),加入原有的過山車的路徑a[]中,讓自己的血壓拉得儘可能高。
請計算一下,小明的血壓最高可以變成多少呢?這非常重要。我們需要這個數據來請一位適合的心血管內科醫生。救救小明吧!您的AC非常重要!


再反手給一個知乎的原問題鏈接 —— 

https://www.zhihu.com/question/355256075/answer/907869387

當然,事情前前後後的鏈接可多了2333 ~


以下內容的發表時間是 —— 2019年11月25日 20:30:07

血壓遊戲,實質名歸好吧233333~
代碼時間是 2019-11-23 18:34:27一個標點符號我都沒有修改,貼在這裏了。

詢問了出題組包括bin巨,他們同意我把標程發過來。
不排除有錯誤,我覺得很有可能會被發現哪裏寫得有問題,但是能學到知識不是美滋滋嗎 ~
而且確實,在我看來,就與我寫了更優複雜度的標程沒什麼區別,除了n的設置外,沒有影響比賽結果。問心無愧。

// #include <bits/stdc++.h>
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#include<time.h>
using namespace std;
#define FMS(x, y, g) memset(x, y, sizeof(x[0]) * (g))
typedef long long LL;

// Variables For the Specific Problem
const bool GUESS = false;                // 是否驗證一些猜想(如點數、邊數等)的開關
const bool DEBUG = false;               // 是否輸出細節(用於調試)的開關
// control variables end
const LL INF = 1e16;                    // 表示兩點之前的極大收益 [1E9是不夠的哦]
const int E9 =  1e9;                    // 就表示 1E9
const int G = 100000;                     // 題目設置的數組長度
const int N = G * 4 + 99;               // 點數 4n 級別
const int M = G * 10 * 2 + 99;          // 邊數 10n 級別
int n, m;                               // n 表示原始數組 a 的大小,m 表示可插入集合 b 的大小
int a[N], b[N];                         // 待插入數組 a, 可插入集合 b
int casenum, casei;                     // 數據組數 casenum, 當前數據編號 casei
// End

// 把 a 或 b 的信息整合一起的數據類型
struct Ele
{
    int tp, id;
    int l, r;
    Ele(){};
    Ele(int tp_, int id_, int l_, int r_) {
        tp = tp_; id = id_; l = l_; r = r_;
    };
};
// 按照左界下降排序
bool cmpL(Ele a, Ele b) {
    if(a.l != b.l)return a.l > b.l;
    return a.tp > b.tp;
}
// 按照右界上升排序
bool cmpR(Ele a, Ele b) {
    if(a.r != b.r)return a.r < b.r;
    return a.tp > b.tp;
}

// 費用流
struct wkcMCMF {
    int ST, ED;                             // 源點與匯點(我習慣上使得ST=0,ED=最後一個點的編號)
    int first[N], ID;                       // 邊集的起點邊編號(ID初始化爲1,表示新分配的邊的編號)
    int w[M], cap[M], cost[M], nxt[M];	    // 邊包括了(抵達點w,容量cap,單位流量成本cost,下條邊編號nxt)等信息
    LL f[N];							    // f[x]表示在殘量網絡下,從源點到達x的最小距離
    int pe[N];							    // pe[x]記錄流向x的前驅邊
    bool e[N];							    // e[x]是判定點x是否在SPFA隊列中的輔助數組
    queue<int> q;                           // q是SPFA的隊列
    int SUM;                                // 用於檢查程序正確性——求最終血壓值

    // 加邊
    void ins(int x, int y, int cap_, LL cost_) {
        w[++ID] = y;
        cap[ID] = cap_;
        cost[ID] = cost_;
        nxt[ID] = first[x];
        first[x] = ID;
        w[++ID] = x;
        cap[ID] = 0;
        cost[ID] = -cost_;
        nxt[ID] = first[y];
        first[y] = ID;
    }

    // 入隊
    void inq(int x, LL cost_, int pe_) {
        if (cost_ <= f[x])return;           // 單位流量收益更小,沒有更新意義
        f[x] = cost_;                       // 單位流量收益大的條件下做更新
        pe[x] = pe_;                        // 然後記錄上一條邊
        if (x == ED || e[x])return;         // SPFA的入隊標記以防止重複入隊做冗餘更新
        e[x] = true;
        q.push(x);
    }

    // SPFA找最長路(最高收益)
    int Augmenting = 1;                     // 表示具有增廣意義的最低增廣收益值,可能會有調整爲 1 或者 0 的需要
    bool spfa() {                           // 返回值爲true表示找到了一條成功的增廣路
        FMS(f, -63, ED + 2);         // 初始的收益值一定要設置爲極小 [-1似乎是不夠的]
        cap[0] = E9;
        inq(ST, 0, 0);
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            e[x] =  0;
            for (int z = first[x]; z; z = nxt[z]) {
                if (cap[z])inq(w[z], f[x] + cost[z], z);
            }
        }
        return f[ED] >= Augmenting;         // 收益 < Augmenting 之後的增廣便不再有意義了
    }

    // 從終點滾到起點,確定此費用下的最大流量,並修改殘餘流量 [單路回溯的普通寫法]
    vector<LL>slowMCMF(LL basic) {
        vector<LL>rtn;
        int maxflow = 0;
        LL mincost = basic;
        while (spfa()) {
            int flow = E9;
            int x = ED;
            while (x != ST) {
                flow = min(flow, cap[pe[x]]);
                x = w[pe[x] ^ 1];
            }
            maxflow += flow;
            x = ED;
            while (x != ST) {
                cap[pe[x]] -= flow;
                cap[pe[x] ^ 1] += flow;
                x = w[pe[x] ^ 1];
            }
            for(int i = 1; i <= flow; ++i) {
                mincost += f[ED];
                rtn.push_back(mincost);
            }
        }
        while(rtn.size() < m) {
            rtn.push_back(mincost);
        }
        return rtn;
    }

    // 高效費用流所使用的DFS多路回溯算法
    bool vis[N];
    int dfs(int x, int all) {
        if (x == ST)return all;
        int use = 0;
        vis[x] = true;
        for (int z = first[x]; z; z = nxt[z])
            if (cap[z ^ 1]) {
                int y = w[z];
                if (!vis[y] && f[y] + cost[z ^ 1] == f[x]) {
                    int tmp = dfs(y, min(cap[z ^ 1], all - use));
                    cap[z ^ 1] -= tmp;
                    cap[z] += tmp;
                    use += tmp;
                    if (use == all)break;
                }
            }
        return use;
    }

    // 從終點滾到起點,確定此費用下的最大流量,並修改殘餘流量 [多路回溯的DFS高效寫法]
    vector<LL> fastMCMF(LL basic) {
        vector<LL>rtn;
        int maxflow = 0;
        LL mincost = basic;
        while (spfa()) {
            int flow;
            while (FMS(vis, 0, ED + 2),
                    flow = dfs(ED, E9)) {
                maxflow += flow;
                for(int i = 1; i <= flow; ++i) {
                    mincost += f[ED];
                    rtn.push_back(mincost);
                }
            }
        }
        while(rtn.size() < m) {
            rtn.push_back(mincost);
        }
        return rtn;
    }

    // ST = 0
    // added point [1, m]
    // up segment point [m + 1, m + n)
    // down segment point [m + n + 1, m + n + n)
    // final [m + n + n + 0, m + n + n + n]
    // ED = m + n * 3 + 1
    Ele ele[N];
    map<int, int>rkL;                   // 記錄每個左界區間對應的排名(start from 1)
    map<int, int>rkR;                   // 記錄每個右界區間對應的排名(start from 1)
    pair<int, int>vaL[N];               // sorted L list (vaL, id) down
    pair<int, int>vaR[N];               // sorted R list (vaR, id) up
    int pos[N];                         // a to b, record the ans
    vector<int>ansVec;                  // ans vector
    void NplusM_mapBuild() {
        ID = 1;
        ST = 0;
        ED = m + n * 3 + 1;
        int eg = 0;
        for (int i = 1; i <= m; ++i) {
            ele[++eg].tp = 1;
            ele[eg].id = i;
            ele[eg].l = ele[eg].r = b[i];
        }
        for (int i = 1; i < n; ++i) {
            ele[++eg].tp = 2;
            ele[eg].id = i;
            ele[eg].l = min(a[i], a[i + 1]);
            ele[eg].r = max(a[i], a[i + 1]);
        }
        rkL.clear();
        rkR.clear();
        // point -> bigger segment [L decreasing order]
        // 從區間 [l[i + 1], r[i + 1]] 向區間 [l[i], r[i]] 連一條容量極大(>=m即可),收益爲(l[i] - l[i + 1]) * 2的邊 <此處n - 2條邊>
        int lastId = E9;
        int lastV;
        int oL = 0;
        sort(ele + 1, ele + eg + 1, cmpL);
        for(int i = 1; i <= eg; ++i) {
            if(ele[i].tp == 2) {
                int eid = ele[i].id + m;
                if(lastId != E9) {
                    ins(eid, lastId, E9, (lastV - ele[i].l) << 1);
                }
                lastId = eid;
                lastV = ele[i].l;
                vaL[++oL] = {lastV, ele[i].id};
                rkL[lastV] = oL;
            }
            else if(lastId != E9) {
                // 向比其大的第一個(如果存在) "下匹配的左界區間[l, r]", 連一條容量爲1(>=1即可),收益爲(l - v) * 2的邊
                ins(ele[i].id, lastId, 1, (lastV - ele[i].l) << 1);
            }
        }
        if (DEBUG) {
            printf("rkL(v,g): ");
            for(auto &it : rkL)printf("[%d %d] ", it.first, it.second);
            puts("");
        }
        // point -> smaller segment [R increasing order]
        // 從區間 [l[i + 1], r[i + 1]] 向區間 [l[i], r[i]] 連一條容量極大(>=m即可),收益爲(r[i + 1] - r[i]) * 2的邊 <此處n - 2條邊>
        lastId = E9;
        int oR = 0;
        sort(ele + 1, ele + eg + 1, cmpR);
        for (int i = 1; i <= eg; ++i) {
            if (ele[i].tp == 2) {
                int eid = ele[i].id + m + n;
                if(lastId != E9) {
                    ins(eid, lastId, E9, (ele[i].r - lastV) << 1);
                }
                lastId = eid;
                lastV = ele[i].r;
                vaR[++oR] = {lastV, ele[i].id};
                rkR[lastV] = oR;
            }
            else if (lastId != E9) {
                // 向比其大的第一個(如果存在) "下匹配的左界區間[l, r]", 連一條容量爲1(>=1即可),收益爲(v - r) * 2的邊
                ins(ele[i].id, lastId, 1, (ele[i].r - lastV) << 1);
            }
        }
        if (DEBUG) {
            printf("rkR(v,g): ");
            for(auto &it : rkR)printf("[%d %d] ", it.first, it.second);
            puts("");
        }
        // 從 m 個插入點向首尾 2 個特殊插入位置連一條容量爲1(>=1即可),收益爲0的邊 <此處m * 2條邊>
        for (int i = 1; i <= m; ++i) {
            ins(i, m + n + n + 0, 1, abs(b[i] - a[1]));
            ins(i, m + n + n + n, 1, abs(b[i] - a[n]));
        }
        // 源點連接 m 個被插入點[1, m], 容量統一爲1,收益統一爲0 <此處m條邊>
        for (int i = 1; i <= m; ++i) {
            ins(ST, i, 1, 0);
        }
        // 下匹配左區間點 & 上匹配右區間點 -> 真實區間點 <此處(n-1)*2條邊>
        for (int i = 1; i < n; ++i) {
            ins(m + i, m + n + n + i, 1, 0);
            ins(m + n + i, m + n + n + i, 1, 0);
        }
        // 真實區間點 [m + n + n + 0, m + n + n + n] 向匯點 ED 連一條容量爲1,收益爲0的邊 <此處n + 1條邊>
        for (int i = 0; i <= n; ++i) {
            ins(m + n + n + i, ED, 1, 0);
        }
    }

    // 根據網絡流的流量情況構造解
    int from[N];                    // 記錄每個實際的區間a是作爲上升區間還是下降區間被匹配的
    void NplusM_output() {
        set<int>AnyPosOK_bset;
        for (int i = 1; i <= m; ++i) {
            AnyPosOK_bset.insert(b[i]);
        }
        vector<Ele>LbTOa;
        vector<Ele>RbTOa;
        for (int y = 0; y <= n; ++y) {
            // check[m + 1, m + n) && [m + n + 1, m + n + n)
            if (y != 0 && y != n) {
                int va = min(a[y], a[y + 1]);
                for (int z = first[m + y]; z; z = nxt[z]) {
                    if (z % 2 == 1 && cap[z] != 0 && w[z] <= m) {
                        int vb = b[w[z]];
                        int va = min(a[y], a[y + 1]);
                        AnyPosOK_bset.erase(vb);
                        LbTOa.push_back({vb, rkL[va], va, va});
                        if (DEBUG) {
                            printf("LbTOa: (b = %d a = %d) va = %d vb = %d\n", w[z], y, va, vb);
                        }
                    }
                }
                va = max(a[y], a[y + 1]);
                for (int z = first[m + n + y]; z; z = nxt[z]) {
                    if (z % 2 == 1 && cap[z] != 0 && w[z] <= m) {
                        int vb = b[w[z]];
                        AnyPosOK_bset.erase(vb);
                        RbTOa.push_back({vb, rkR[va], va, va});
                        if (DEBUG) {
                            printf("RbTOa: (b = %d a = %d) va = %d vb = %d\n", w[z], y, va, vb);
                        }
                    }
                }
                // 查詢每個區間實際是匹配的上升區間還是下降區間
                from[y] = 0;
                for (int z = first[m + n + n + y]; z; z = nxt[z]) {
                    if (z % 2 == 1 && cap[z] != 0) {
                        if (w[z] > m && w[z] <= m + n) {
                            from[y] = 1;
                        } else if (w[z] > m + n && w[z] <= m + n + n) {
                            from[y] = 2;
                        }
                    }
                }
            }
            // 這裏其實只有 y == 0 或 y == n 時纔會被直接從[1, m]產生流量 [TODO——驗證猜想]
            for (int z = first[m + n + n + y]; z; z = nxt[z]) {
                if (z % 2 == 1 && cap[z] != 0 && w[z] <= m) {
                    int vb = b[w[z]];
                    AnyPosOK_bset.erase(vb);
                    break;
                }
            }
        }
        // In LbTOa or RbTOa, {tp:vb, id:rk, l:va, r:va}, the same value makes rk seem to be bigger
        // In vaL or vaR, {vL or vR, id}]
        // vaL[i], the i-th smallest val and pos
        // vaR[i], the i-th biggest val and pos
        FMS(pos, 0, n + 2);
        int LpreID = 0;
        sort(LbTOa.begin(), LbTOa.end(), cmpL); // vaL is going down after sorting
        for(auto &it : LbTOa) {
            LpreID = min(it.id, LpreID + 1);
            while(true) {
                int p = vaL[LpreID].second;
                if (from[p] != 1)++LpreID;
                else {
                    pos[p] = it.tp;
                    break;
                }
            }
        }
        int RpreID = 0;
        sort(RbTOa.begin(), RbTOa.end(), cmpR); // vaR is going up after sorting
        for(auto &it : RbTOa) {
            it.id = min(it.id, RpreID + 1);
            RpreID = it.id;
            while(true) {
                int p = vaR[RpreID].second;
                if (from[p] != 2)++RpreID;
                else {
                    pos[p] = it.tp;
                    break;
                }
            }
        }
        if (DEBUG) {
            printf("POS: ");
            for(int i = 1; i < n; ++i)printf("%d ", pos[i]);
            puts("");
        }
        // ans output
        ansVec.clear();
        for (int y = 0; y <= n; ++y) {
            if (y) {
                ansVec.push_back(a[y]);
            }
            // possibility 1: matched already
            if (y != 0 && y != n && pos[y]) {
                ansVec.push_back(pos[y]);
                continue;
            }
            // possibility 2: head or tail
            bool flag = 0;
            if (y == 0 || y == n) {
                for (int z = first[m + n + n + y]; z; z = nxt[z]){
                    if (z % 2 == 1 && cap[z] != 0 && w[z] <= m) {
                        ansVec.push_back(b[w[z]]);
                        flag = 1;
                        break;
                    }
                }
            }
            // possibility 3: any position is the same to some elements
            if(!flag && AnyPosOK_bset.size()) {
                ansVec.push_back(*AnyPosOK_bset.begin());
                AnyPosOK_bset.erase(AnyPosOK_bset.begin());
            }
        }
        SUM = 0;
        for(int i = 0; i < ansVec.size(); ++i) {
            printf("%d ", ansVec[i]);
            if (i)SUM += abs(ansVec[i] - ansVec[i - 1]);
        }puts("");
    }

    // 低效建圖法,邊數O(nm)
    void NmultM_mapBuild() {
        ID = 1;
        ST = 0;
        ED = m + 1 + n + 1;
        for (int i = 1; i <= m; ++i) {
            int V = b[i];
            ins(ST, i, 1, 0);
            for (int j = 0; j <= n; ++j) {
                int lftV = j == 0 ? V : a[j];
                int rgtV = j == n ? V : a[j + 1];
                int oriV = (j == 0 || j == n) ? 0 : abs(a[j + 1] - a[j]);
                int incV = abs(lftV - V) + abs(rgtV - V) - oriV;
                ins(i, m + 1 + j, 1, incV);
            }
        }
        for (int i = 0; i <= n; ++i) {
            ins(m + 1 + i, ED, 1, 0);
        }
    }

    // 低效建圖下的解構造
    void NmultM_output() {
        // ST = 0
        // b[] ∈ [1, m]
        // a[] ∈ [m + 1 + 0, m + 1 + n]
        // ED = n + m + 2
        vector<int>ansVec;
        for (int i = 0; i <= n; ++i) {
            pos[i] = 0;
        }
        for (int x = 1; x <= m; ++x) {
            for (int z = first[x]; z; z = nxt[z]) {
                if (z % 2 == 0 && cap[z] == 0) {
                    pos[w[z] - m - 1] = b[x];
                }
            }
        }
        for (int i = 0; i <= n; ++i) {
            if(i) ansVec.push_back(a[i]);
            if(pos[i])ansVec.push_back(pos[i]);
        }
        for(auto &it : ansVec) {
            printf("%d ", it);
        }puts("");
    }

    // 輸出實際的流量網絡圖的DEBUG過程
    void printMap() {
        for (int x = 0; x <= ED; ++x) {
            for (int z = first[x]; z; z = nxt[z]) {
                if (z % 2 == 0) {
                    int y = w[z];
                    if (cap[z ^ 1] != 0)
                        printf("%d->%d(%d, %d)\n", x, y, cap[z ^ 1], cost[z]);
                }
            }
        }
    }
}mcmf;

//暴力DFS算法
struct My_BF {
    int rev[1 << 20];
    LL MAXV;
    vector<LL>BEST;
    vector<int>ansVec, vec;
    void init() {
        for(int i = 0; i < 20; ++i) {
            rev[1 << i] = i;
        }
    }
    int lowbit(int x) {
        return x & -x;
    }
    void dfs(int mask, int pos, LL sumV, int num) {
        if (num == m && sumV > MAXV) {
            MAXV = sumV;
            ansVec = vec;
        }
        if (sumV > BEST[num - 1]) {
            BEST[num - 1] = sumV;
        }
        if (pos > n) {
            return;
        }
        for (int tmp = mask; tmp; tmp -= lowbit(tmp)) {
            int o = rev[lowbit(tmp)];
            int v = b[o + 1];
            int addV = 0;
            if (pos == 0) {
                addV = abs(v - a[pos + 1]);
            }
            else if (pos == n) {
                addV = abs(v - a[pos]);
            }
            else {
                addV = abs(v - a[pos + 1]) + abs(v - a[pos]);
            }
            vec.push_back(v); if (pos < n)vec.push_back(a[pos + 1]);
            dfs(mask - lowbit(tmp), pos + 1, sumV + addV, num + 1);
            vec.pop_back(); if (pos < n)vec.pop_back();
        }
        int addV = 0;
        if (pos != 0 && pos != n) {
            addV = abs(a[pos] - a[pos + 1]);
        }
        if (pos < n)vec.push_back(a[pos + 1]);
        dfs(mask, pos + 1, sumV + addV, num);
        if (pos < n)vec.pop_back();
    }
    vector<LL> solve() {
        init();
        BEST.resize(m);
        for (int i = 0; i <= m; ++i) {
            BEST[i] = -1;
        }
        MAXV = -1;
        dfs((1 << m) - 1, 0, 0, 0);
        return BEST;
    }
    void output() {
        for (int i = 0; i < ansVec.size(); ++i) {
            printf("%d ", ansVec[i]);
        }
        puts("");
    }
}bf;

// 貪心算法——TODO
struct My_Greedy {
    LL solve() {
        return 0;
    }
}greedy;

// 數據生成器
struct My_DataGenerator {
    void rd_dataGenerator() {
        srand(time(0));
        freopen("Blood Pressure Game.in", "w", stdout);
        casenum = 1000; printf("%d\n", casenum + 0);
        int smlCasenum = 990;
        int midDCasenum = 997;
        for (casei = 1; casei <= casenum; ++casei)
        {
            int n = rand() % 100 + 1;
            if (casei > smlCasenum) {
                n = rand() % 600 + 1;
            }
            else if(casei > midDCasenum) {
                n = 600;
            }
            m = rand() % (n + 1) + 1;
            int TOPV = casei <= smlCasenum ? n * 10 : (rand() % 2 ? 10000 : E9);
            printf("%d %d\n", n, m);
            set<int>noEqualSot;
            for(int i = 1; i <= n; ++i) {
                do
                {
                    a[i] = rand() % TOPV + 1;
                }while(noEqualSot.count(a[i]));
                noEqualSot.insert(a[i]);
                printf("%d ", a[i]);
            }puts("");
            for(int i = 1; i <= m; ++i) {
                do
                {
                    b[i] = rand() % TOPV + 1;
                }while(noEqualSot.count(b[i]));
                noEqualSot.insert(b[i]);
                printf("%d ", b[i]);
            }puts("");
        }
    }
}dataGenerator;

struct Checker {
    string check_it() {
        auto& ansVec = mcmf.ansVec;
        set<int>sot;
        for (int i = 0; i < n + m; ++i) {
            int v = ansVec[i];
            if(sot.count(v)) {
                return "same value element in ansVec[]";
            }
            sot.insert(v);
        }
        // array check
        set<int>bset;
        for (int i = 1; i <= m; ++i) {
            bset.insert(b[i]);
        }
        int nxtPos = 1;
        int bnum = 0;
        for(int i = 0; i < n + m; ++i) {
            if (nxtPos <= n && a[nxtPos] == ansVec[i]) {
                ++nxtPos;
                bnum = 0;
            }
            else {
                if(!bset.count(ansVec[i])) {
                    return "it is a wrong final array %d";
                }
                bset.erase(ansVec[i]);
                if (++bnum > 1) {
                    return "can not insert continuous elements of array b";
                }
            }
        }
        return "AC";
    }
}checker;

void printVec(string str, vector<LL>vec) {
    if (str != "") printf("%s: ", str.c_str());//cout << str << ": ";
    for(auto &it : vec) {
        printf("%lld ", it);
    }puts("");
}
void printVec(string str, vector<int>vec) {
    if (str != "") printf("%s: ", str.c_str());//cout << str << ": ";
    for(auto &it : vec) {
        printf("%d ", it);
    }puts("");
}

void HumanData()
{
    srand(time(0));
    freopen("Human Data.in", "w", stdout);
    casenum = 8; printf("%d\n", casenum);
    for (casei = 1; casei <= casenum; ++casei) {
        set<int>sot;
        n = 600; m = 601;
        printf("%d %d\n", n, m);
        int basic = 5000 + rand() % 1000;
        int dif = rand() % 3 + 2;
        a[1] = basic; sot.insert(a[1]);
        a[2] = basic + 1; sot.insert(a[2]);
        for(int i = 3; i <= n; ++i){
            if (i & 1)a[i] = a[i - 2] - dif;
            else a[i] = a[i - 2] + dif;
            sot.insert(a[i]);
        }
        for(int i = 1; i <= n; ++i) {
            printf("%d ", a[i]);
        }puts("");
        for(int i = 1; i <= m; ++i) {
            do{
                b[i] = rand() % (basic + dif * n) + 1;
            }while(sot.count(b[i]));
            sot.insert(b[i]);
            printf("%d ", b[i]);
        }puts("");
    }
}

int main() {
    // HumanData(); return 0;
    // dataGenerator.rd_dataGenerator(); return 0;
    freopen("Blood Pressure Game.in", "r", stdin); freopen("Blood Pressure Game.out", "w", stdout);
    // freopen("Human Data.in", "r", stdin);
    scanf("%d", &casenum); for(casei = 1; casei <= casenum; ++casei) {
        printf("Case #%d:\n", casei);
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        for (int i = 1; i <= m; ++i) scanf("%d", &b[i]);
        LL basic = 0;
        for (int i = 2; i <= n; ++i) {
            basic += abs(a[i] - a[i - 1]);
        }

        // solution 1: fastNetworkFlow
        bool NplusM_MCMF = true;
        vector<LL> fastMCMFvec;
        if (NplusM_MCMF) {
            mcmf.NplusM_mapBuild();
            fastMCMFvec = mcmf.fastMCMF(basic);
            printVec("", fastMCMFvec); //fastMCMFvec
            mcmf.NplusM_output();
            FMS(mcmf.first, 0, mcmf.ED + 2);
            // ans checker
            string check_str = checker.check_it();
            if(check_str != "AC") {
                puts(check_str.c_str());
                for (int i = 1; i <= n; ++i) {
                    printf("%d ", a[i]);
                }puts("");
                for (int i = 1; i <= m; ++i) {
                    printf("%d ", b[i]);
                }puts("");
                printf("SUM = %lld\n", mcmf.SUM);
                while(true);
            }
            if(DEBUG) {
                mcmf.printMap();
            }
        }

        // solution 2: slowNetworkFlow
        bool NmultM_MCMF = false;
        vector<LL> slowMCMFvec;
        if (NmultM_MCMF) {
            mcmf.NmultM_mapBuild();
            slowMCMFvec = mcmf.slowMCMF(basic);
            printVec("", slowMCMFvec); // slowMCMFvec
            mcmf.NmultM_output();
            FMS(mcmf.first, 0, mcmf.ED + 2);
            if (NplusM_MCMF && slowMCMFvec != fastMCMFvec) {
                puts("Error: slowMCMFvec != fastMCMFvec");
                while(true);
            }
        }

        // solution 3: Brute Force (DFS)
        bool bruteForce = false;
        if (bruteForce) {
            vector<LL> BFvec = bf.solve();
            printVec("BFvec", BFvec);
            bf.output();
            if (NplusM_MCMF && BFvec != fastMCMFvec) {
                puts("Error: BFvec != fastMCMFvec");
                while(true);
            }
        }

        // solution 4: Greedy
        // LL ans_greedy = greedy.solve();
    }
    return 0;
}
/*
 【Trick && Tsukkomi】

 Input
 1
 4 5
 21 3 48 39
 16 66 9 64 36
 Output
 36 21 66 3 64 48 9 39 16

 Input
 1
 4 4
 10 50 3 6
 1 9 23 5
 Output
 150
 5 10 1 50 3 23 6 9

 【題意】
 把 m 個數任意插入到長度爲 n 的數組中的縫隙或兩側,在每個位置最多隻能插入一個數的條件下,使得相鄰數之差的絕對值的和儘可能大。

 【分析】
 這道題,題目是將 m 個待插入數值,向 n + 1 個區間做插入。而這個插入其實也就是匹配。這種帶權匹配問題,我們可以使用網絡流(費用流)算法來解決。
 因爲我們希望最終的差值之和儘可能大,所以這個"費用"此處是"收益",我把它稱呼爲最大收益最大流好啦。

 因爲匹配的可能是 n * m ,這個圖實際構成了"完全二分圖",邊數是m * (n + 1).
 然而,面對1000的數據規模,O(nm) 的邊數就已經巨大無比了,最終算法的複雜度將會難以喫得消。
 要怎麼辦纔好呢?我們可以結合這道題的特殊性,優化建圖!
 可以看到——除了首尾這兩個特殊的插入位置外,其他所有的插入位置都可以用一個二元對[l, r]來表示。
 如果插入的數值 v 比 l 小,其實收益只與 l 有關,是 (l - v) * 2
 如果插入的數值 v 比 r 大,其實收益只與 r 有關,是 (v - r) * 2
 否則,插入的數值在區間內,則該插入操作不會產生任何收益。

 顯然,我們發現,對於插入區間,籠統來說,是具有 l 越大優、或 r 越小越優的性質的。
 其實也就是說,如果我們最終做了匹配 v < [l2, r2],那麼不可能我們有一個閒置未匹配區間[l1, r1](l1 > l2)的,這樣 v 匹配[l1, r1]一定更優。
 同理,       如果我們最終做了匹配 [l2, r2] > v,那麼不可能我們有一個閒置未匹配區間[l1, r1](r1 < r2)的,這樣 v 匹配[l1, r1]一定更優
 而對於兩個區間,其替換後價值的收益其實是線性的。如 v < [l2, r2],由[l2, r2]調整爲[l1, r1](l1 > l2)的時候,收益是(l1 - l2) * 2

 發現了這些性質後,我們就可以優化建圖啦——
 (1) 設置源點編號爲0,匯點編號爲 m + n * 3 + 1
 (2) 源點連接 m 個被插入點[1, m], 容量統一爲1,收益統一爲0 <此處m條邊>
 (3) 把所有非兩側的可插入區間,抽象爲[m + 1, m + n)這 n - 1 個點,
    按照左界從大到小(從優到差)排序,我們考慮每個區間都可能匹配(插入)了比它小的數值(v < l)
    從區間 [l[i + 1], r[i + 1]] 向區間 [l[i], r[i]] 連一條容量極大(>=m即可),收益爲(l[i] - l[i + 1]) * 2的邊 <此處n - 2條邊>
 (4) 把所有非兩側的可插入區間,抽象爲[m + n + 1, m + n + n)這 n - 1 個點,
    按照右界從小到大(從優到差)排序,我們考慮每個區間能可能匹配(插入)了比它大的數值(v > r)
    從區間 [l[i + 1], r[i + 1]] 向區間 [l[i], r[i]] 連一條容量極大(>=m即可),收益爲(r[i + 1] - r[i]) * 2的邊 <此處n - 2條邊>
 (5) 然而,一個區間最多隻能匹配一次,即不可能其既作爲較大的區間被插入了值,同時由作爲最小的區間被插入了值。
    因此,我們再設置 [m + n + n + 0, m + n + n + n] 這 n + 1 個點,這些點向匯點 ED 連一條容量爲1,收益爲0的邊 <此處n + 1條邊>
    同時,對於i ∈ [1, n), m + i 與 m + n + i 同時向 m + n + n + i 連一條容量爲1(>=1即可),收益爲0的邊 <此處(n - 1) * 2條邊>
    於是,我們控制使得每個區間被最多匹配一次,同時一個區間不可能同時作爲較大區間和較小區間同時被匹配插入。
 (6) 注意到,可以被插入匹配的位置其實有 n + 1 個,而對於首區間和尾區間,編號實際爲 m + n + n + 0 和 m + n + n + n,
    我們直接從 m 個插入點向這 2 個特殊插入位置連一條容量爲1(>=1即可),收益爲0的邊 <此處m * 2條邊>
 (7) 不要忘記了,"向下匹配的左界區間"和"向上匹配的右界區間",雖然它們都被連成了鏈,且連入了唯一編號的區間,但卻沒有流量流入。
    對於 m 個插入值 v ,向比其大的第一個(如果存在) "下匹配的左界區間[l, r]", 連一條容量爲1(>=1即可),收益爲(l - v) * 2的邊
    同理,             向比其小的第一個(如果存在) "上匹配的右界區間[l, r]", 連一條容量爲1(>=1即可),收益爲(v - r) * 2的邊
    <此處最多m * 2條邊>

 這個圖最終形成啦。
 層次包括六層{源點0}、{插入層[1, m]}、{下匹配左界區間層[m + 1, m + n)}、{上匹配右界區間層[m + n + 1, m + n + n)}、{真實區間層[m + n + n + 0, m + n + n + n]}、{匯點m + n * 3 + 1}
 點數總共2 + m + (n - 1) + (n - 1) + (n + 1)共計m + n * 3 + 1個,即點數爲4n級別
 同時,邊數可以由(1)~(7)求和可得,爲10n級別
 因而,算法的複雜度爲O(點數爲4n邊數爲10n的費用流複雜度) :p

 【數據】
6

2 3
5 11
10 3 1

4 1
1 2 3 4
5

4 2
1 2 3 4
5 6

4 5
1 2 3 4
5 6 7 8 9

4 4
10 50 3 6
1 9 23 5

4 2
10 50 3 6
9 23

 */

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章