Description

CZ 市为了欢迎全国各地的同学, 特地举办了一场盛大的美食节. 作为一个喜欢尝鲜的美食客, 小 M 自然不愿意错过这场盛宴. 他很快就尝遍了美食节所有的美食. 然而, 尝鲜的欲望是难以 满足的. 尽管所有的菜品都很可口, 厨师做菜的速度也很快, 小 M 仍然觉得自己桌上没有已经 摆在别人餐桌上的美食是一件无法忍受的事情. 于是小 M 开始研究起了做菜顺序的问题, 即安排一个做菜的顺序使得同学们的等待时间最短. 小 M 发现, 美食节共有 \(n\) 种不同的菜品. 每次点餐, 每个同学可以选择其中的一个菜品. 总共有 \(m\) 个厨师来制作这些菜品. 当所有的同学点餐结束后, 菜品的制作任务就会分配给每个厨师. 然后每个厨师就会同时开始做菜. 厨师们会按照要求的顺序进行制作, 并且每次只能制作一人份. 此外, 小 M 还发现了另一件有意思的事情: 虽然这 \(m\) 个厨师都会制作全部的 \(n\) 种菜品, 但对于同一菜品, 不同 厨师的制作时间未必相同. 他将菜品用 \(1, 2, \ldots, n\) 依次编号, 厨师用 \(1, 2, \ldots, m\) 依次编号, 将第 \(j\) 个厨师制作第 \(i\) 种菜品的时间记为 \(t_{i,j}\). 小 M 认为: 每个 同学的等待时间为所有厨师开始做菜起, 到自己那份菜品完成为止的时间总长度. 换句话说, 如果一个同学点的菜是某个厨师做的第 \(k\) 道菜, 则他的等待时间就是这个厨师制作前 \(k\) 道菜的时间之和. 而总等待时间为所有同学的等待时间之和. 现在, 小 M 找到了所有同学的 点菜信息: 有 \(p_i\) 个同学点了第 \(i\) 种菜品 (\(1 \leq i \leq n\)) . 他想知道的是 最小的总等待时间是多少.

Input

输入文件的第 \(1\) 行包含两个正整数 \(n\)\(m\), 表示菜品的种数和厨师的数量.

\(2\) 行包含 \(n\) 个正整数, 其中第 \(i\) 个数为 \(p_i\), 表示点第 \(i\) 种菜品的人数.

接下来有 \(n\) 行, 每行包含 \(m\) 个非负整数, 这 \(n\) 行中的第 \(i\) 行的第 \(j\) 个数为 \(t_{i,j}\), 表示第 \(j\) 个厨师制作第 \(i\) 种菜品所需的时间.

输入文件中每行相邻的两个数之间均由一个空格隔开, 行末均没有多余空格.

Output

输出仅一行包含一个整数, 为总等待时间的最小值.

Sample Input

3 2
3 1 1
5 7
3 6
8 9

Sample Output

47

Data Range

对于 100% 的数据,\(n \leq 40, m \leq 100, p \leq 800, t_{i,j} \leq 1000\), (其中 \(p = \sum p_i\), 即点菜同学的总人数).

每组数据的 \(n, m, p\) 值如下:

ID \(n\) \(m\) \(p\)
\(1\) \(5\) \(5\) \(10\)
\(2\) \(40\) \(1\) \(400\)
\(3\) \(40\) \(2\) \(300\)
\(4\) \(40\) \(40\) \(40\)
\(5\) \(5\) \(40\) \(100\)
\(6\) \(10\) \(50\) \(200\)
\(7\) \(20\) \(60\) \(400\)
\(8\) \(40\) \(80\) \(600\)
\(9\) \(40\) \(100\) \(800\)
\(10\) \(40\) \(100\) \(800\)

Explanation

对于打算要正解分的人给他骗分算法他也不看, 不打算全要分的人给他正解也不看, 就是 这么套路==

现在想这个问题, 没有做过 SCOI2007 修车 (bzoj1070, 无传送门) 的请出门右转先去做那道题, 不然这道没法做 QwQ

会了那道题这道题还是做不出来 QwQ

其实是动态加边, 因为之前有些边根本用不上, 还在 SPFA 里面白跑吃时间......

但是动态加边有技巧, 比如我看了一种 (hzwer) 很正常的动态加边, 而且很好理解, 并且 十分的 OOP, 但是以我的巨大常数写出来就是个噩梦~

开了 -O2 用了 STL 队列比 hzwer 慢一倍, 用了自己写的队列还是比他慢上 10% 左右, 而且还是在开优化的情况下, 这样明显交不过, 魔改了好几次以后无限 TLE, 终于失去了信心.

后来发现另外一种动态加边的方式: 他把源汇的方向反过来 (这样似乎可以减小搜索深度, 所以虽然复杂度一样但是实际是要快上四倍左右的), 然后就是一些奇怪的加边方法, 不明觉厉.

讲真我的代码里为了更 OOP 写上 callback(...) 这类的函数真的好吗=n=

还是需要思考一下的. 如果想用哪种更慢的方式, 就把 #define USE_FAST_APPROACH 注释 掉, 然后加上一行 #define USE_GENERAL_APPROACH 就可以了 (我觉得诸位大神应该能 看懂吧 orz)

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cstring>

#define SWTAPI inline
#define USE_FAST_APPROACH
using namespace std;
typedef long long lli;
const int maxn = 100100, maxm = 3000100, maxN = 110;
const lli infinit = 0x3f3f3f3f;

inline lli read(void)
{
    lli x = 0, f = 1; char ch=getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

template <typename _Type>
class fast_queue
{
public:
    _Type arr[maxn];
    int beg, end;
    SWTAPI _Type front(void) {
        return arr[beg]; }
    SWTAPI void push(const _Type& val) {
        arr[end++] = val;
        if (end >= maxn) end = 0;
        return ; }
    SWTAPI void pop(void) {
        beg += 1;
        if (beg >= maxn) beg = 0;
        return ; }
    SWTAPI bool empty(void) {
        return beg == end; }
    fast_queue(void) {
        beg = end = 0;
        return ;
    }
};

class SpfaMaxFlow
{
public:
    struct edge
    {
        int u, v;
        lli flow, cost;
        edge *next, *rev;
    };
    int n, s, t, ecnt;
    edge *edges[maxn], *from[maxn], epool[maxm];
    SWTAPI void add_edge(int u, int v, lli flow, lli cost)
    {
        edge *p = &epool[++ecnt],
             *q = &epool[++ecnt];
        p->u = u; p->v = v; p->flow = flow; p->cost = cost;
        p->next = edges[u]; edges[u] = p; p->rev = q;
        q->u = v; q->v = u; q->flow = 0; q->cost = -cost;
        q->next = edges[v]; edges[v] = q; q->rev = p;
        return ;
    }
    bool inque[maxn];
    lli dist[maxn];
    SWTAPI bool bfs(void)
    {
        for (int i = 0; i <= n; i++)
            dist[i] = infinit, inque[i] = false, from[i] = NULL;
        static fast_queue<int> que;
        que.push(s);
        inque[s] = true;
        dist[s] = 0;
        // Emulating a queue, manually
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (edge *ep = edges[p]; ep; ep = ep->next)
                if (ep->flow && dist[p] + ep->cost < dist[ep->v]) {
                    dist[ep->v] = dist[p] + ep->cost;
                    if (!inque[ep->v]) {
                        que.push(ep->v);
                        inque[ep->v] = true;
                    }
                    from[ep->v] = ep;
                }
            inque[p] = false;
        }
        if (dist[t] < infinit)
            return true;
        return false;
    }
    SWTAPI lli spfa(void(&callback)(int))
    {
        lli res = 0;
        while (bfs()) {
            lli flow = infinit;
            int targ = 0;
            for (edge *ep = from[t]; ep; ep = from[ep->u]) {
                flow = min(flow, ep->flow);
                if (!ep->u) targ = ep->v;
            }
            for (edge *ep = from[t]; ep; ep = from[ep->u]) {
                ep->flow -= flow;
                ep->rev->flow += flow;
            }
            res += dist[t] * flow;
            callback(targ);
        }
        return res;
    }
} graph;

int m, n;
lli c[maxN], C, t[maxN][maxN];
int num[maxN], pos[maxN];

SWTAPI void build_dynamic(int y)
{
    #ifdef USE_GENERAL_APPROACH
    int a = (y-1) / C + 1,
        b = y % C + 1;
    for (int i = 1; i <= m; i++)
        graph.add_edge((a-1)*C+b, n*C+i, 1, b*t[i][a]);
    #endif
    #ifdef USE_FAST_APPROACH
    static int tot = m+n;
    int j = 1;
    for (; j <= n && graph.epool[pos[j]].flow <= 0; j++);
    num[j] += 1, tot += 1;
    graph.add_edge(tot, graph.t, 1, 0);
    pos[j] = graph.ecnt;
    for (int i = 1; i <= m; i++)
        graph.add_edge(i, tot, 1, num[j] * t[i][j]);
    #endif
    return ;
}

int main(int argc, char** argv)
{
    scanf("%d%d", &m, &n);
    C = 0;
    for (int i = 1; i <= m; i++) {
        c[i] = read();
        C += c[i];
    }
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
            t[i][j] = read();
    // Creating graph (static part)
    #ifdef USE_GENERAL_APPROACH
    graph.n = n*C + m + 1;
    graph.s = 0;
    graph.t = graph.n;
    for (int i = 1; i <= n*C; i++)
        graph.add_edge(graph.s, i, 1, 0);
    for (int i = 1; i <= m; i++)
        graph.add_edge(n*C + i, graph.t, c[i], 0);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            graph.add_edge((i-1)*C+1, n*C+j, 1, t[j][i]);
    #endif
    #ifdef USE_FAST_APPROACH
    graph.n = m+n+C+2;
    graph.s = graph.n - 1;
    graph.t = graph.n;
    for (int i = 1; i <= m; i++)
        graph.add_edge(graph.s, i, c[i], 0);
    for (int j = 1; j <= n; j++) {
        graph.add_edge(m+j, graph.t, 1, 0);
        num[j] = 1, pos[j] = graph.ecnt;
        for (int i = 1; i <= m; i++)
            graph.add_edge(i, m+j, 1, t[i][j]);
    }
    #endif
    // Running maximum flow and dynamically creating edges
    lli res = graph.spfa(build_dynamic);
    // Retrieved result
    printf("%lld\n", res);
    return 0;
}