1. 程式人生 > >P3749 [六省聯考2017]壽司餐廳 最小割

P3749 [六省聯考2017]壽司餐廳 最小割

\(\color{#0066ff}{ 題目描述 }\)

Kiana 最近喜歡到一家非常美味的壽司餐廳用餐。

每天晚上,這家餐廳都會按順序提供 \(n\) 種壽司,第 \(i\) 種壽司有一個代號 \(a_i\) 和美味度 \(d_{i, i}\) ,不同種類的壽司有可能使用相同的代號。每種壽司的份數都是無限的,\(Kiana\) 也可以無限次取壽司來吃,但每種壽司每次只能取一份,且每次取走的壽司必須是按餐廳提供壽司的順序連續的一段,即 \(Kiana\) 可以一次取走第 \(1, 2\) 種壽司各一份,也可以一次取走第 \(2, 3\) 種壽司各一份,但不可以一次取走第 \(1, 3\) 種壽司。

由於餐廳提供的壽司種類繁多,而不同種類的壽司之間相互會有影響:三文魚壽司和魷魚壽司一起吃或許會很棒,但和水果壽司一起吃就可能會肚子痛。因此,\(Kiana\) 定義了一個綜合美味度 \(d_{i, j} \ (i < j)\),表示在一次取的壽司中,如果包含了餐廳提供的從第 \(i\) 份到第 \(j\) 份的所有壽司,吃掉這次取的所有壽司後將獲得的額外美味度。由於取壽司需要花費一些時間,所以我們認為分兩次取來的壽司之間相互不會影響。注意在吃一次取的壽司時,不止一個綜合美味度會被累加,比如若 \(Kiana\) 一次取走了第 \(1, 2, 3\) 種壽司各一份,除了 \(d_{1, 3}\)

以外,\(d_{1, 2}, d_{2, 3}\) 也會被累加進總美味度中。

神奇的是,\(Kiana\) 的美食評判標準是有記憶性的,無論是單種壽司的美味度,還是多種壽司組合起來的綜合美味度,在計入 \(Kiana\) 的總美味度時都只會被累加一次。比如,若 \(Kiana\) 某一次取走了第 \(1, 2\) 種壽司各一份,另一次取走了第 \(2, 3\)種壽司各一份,那麼這兩次取壽司的總美味度為 \(d_{1, 1} + d_{2, 2} + d_{3, 3} + d_{1, 2} + d_{2, 3}\),其中 \(d_{2, 2}\) 只會計算一次。

奇怪的是,這家壽司餐廳的收費標準很不同尋常。具體來說,如果 \(Kiana\)

一共吃過了 \(c \ (c > 0)\) 種代號為 \(x\) 的壽司,則她需要為這些壽司付出 \(mx^2 + cx\) 元錢,其中 \(m\) 是餐廳給出的一個常數。

現在 \(Kiana\) 想知道,在這家餐廳吃壽司,自己能獲得的總美味度(包括所有吃掉的單種壽司的美味度和所有被累加的綜合美味度)減去花費的總錢數的最大值是多少。由於她不會算,所以希望由你告訴她。

\(\color{#0066ff}{輸入格式}\)

第一行包含兩個正整數 \(n, m\),分別表示這家餐廳提供的壽司總數和計算壽司價格中使用的常數。 第二行包含 \(n\) 個正整數,其中第 \(k\) 個數 \(a_k\) 表示第 \(k\) 份壽司的代號。 接下來 \(n\) 行,第 \(i\) 行包含 \(n - i + 1\) 個整數,其中第 \(j\) 個數 \(d_{i, i+j-1}\) 表示吃掉壽司能獲得的相應的美味度,具體含義見問題描述。

\(\color{#0066ff}{輸出格式}\)

輸出共一行包含一個正整數,表示 \(Kiana\) 能獲得的總美味度減去花費的總錢數的最大值。

\(\color{#0066ff}{輸入樣例}\)

10 1
5 5 4 4 1 2 5 1 5 3
83 91 72 29 22 -5 57 -14 -36 -3
-11 34 45 96 32 73 -1 0 29
-48 68 44 -5 96 66 17 74
88 47 69 -9 2 25 -49
86 -9 -77 62 -10 -30
2 40 95 -74 46
49 -52 2 -51
-55 50 -44
72 22
-68

\(\color{#0066ff}{輸出樣例}\)

1223

\(\color{#0066ff}{資料範圍與提示}\)

\(\color{#0066ff}{題解}\)

這是一個最大流最小割的題

要找最大的美味度-花費

我們找到總美味度,用最小割求花費,一減就行了

首先考慮總美味度的問題

因為有負的美味度

考慮將其算在花費裡就行了,因為我們並不知道總美味度裡到底包不包括某些負的值

將其算作花費在網路流上跑,這樣才能保證最大

下面開始花式建邊

建邊

1、原點向每個美味度為正的區間連容量為美味度的邊

2、每個美味度為負的區間像匯點連容量為-美味度的邊

3、每個區間\([i,j]\)向區間\([i+1,j], [i,j-1]\)連一條容量為inf的邊

4、每個區間向區間端點對應的壽司連一條inf的邊

5、每個壽司向匯點連容量為壽司種類編號的邊

6、每個壽司向所屬壽司種類連容量為inf的邊

7、每個壽司種類向匯點連容量為\(m * id * id\)的邊

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; int x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
struct node {
    int to, dis;
    node *nxt, *rev;
    node(int to = 0, int dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
    void *operator new (size_t) {
        static node *S = NULL, *T = NULL;
        return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    }
};
const int maxn = 10505;
const int mod = 150;
const int inf = 0x7fffffff;
int id[mod][mod], val[mod][mod], idd[maxn];
int dep[maxn], a[mod];
node *head[maxn], *cur[maxn];
int n, s, t, m;
int ans;
using std::queue;
queue<int> q;
void add(int from, int to, int dis) {
    head[from] = new node(to, dis, head[from]);
}
void link(int from, int to, int dis) {
    add(from, to, dis);
    add(to, from, 0);
    head[from]->rev = head[to];
    head[to]->rev = head[from];
}
bool bfs() {
    for(int i = s; i <= t; i++) cur[i] = head[i], dep[i] = 0;
    q.push(s);
    dep[s] = 1;
    while(!q.empty()) {
        int tp = q.front(); q.pop();
        for(node *i = head[tp]; i; i = i->nxt) 
            if(!dep[i->to] && i->dis) {
                dep[i->to] = dep[tp] + 1;
                q.push(i->to);
            }
    }
    return dep[t];
}
int dfs(int x, int change) {
    if(x == t || !change) return change;
    int flow = 0, ls;
    for(node *i = cur[x]; i; i = i->nxt) {
        cur[x] = i;
        if(dep[i->to] == dep[x] + 1 && (ls = dfs(i->to, std::min(change, i->dis)))) {
            flow += ls;
            change -= ls;
            i->dis -= ls;
            i->rev->dis += ls;
            if(!change) break;
        }
    }
    return flow;
}
int dinic() {
    int flow = 0;
    while(bfs()) flow += dfs(s, inf);
    return flow;
}
void build() {
    int cnt = 0;
    for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j++)
            id[i][j] = ++cnt;
    static bool vis[maxn];
    for(int i = 1; i <= n; i++)
        if(!vis[a[i]]) {
            vis[a[i]] = true;
            idd[a[i]] = ++cnt;
        }
    memset(vis, 0, sizeof vis);
    s = 0, t = cnt + n + 1;
    for(int i = 1; i <= n; i++) 
        if(!vis[a[i]]) {
            vis[a[i]] = true;
            link(idd[a[i]], t, m * a[i] * a[i]);
        }
    for(int i = 1; i <= n; i++) {
        link(cnt + i, idd[a[i]], inf);
        link(cnt + i, t, a[i]);
    }
    for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j++) {
            if(val[i][j] >= 0) {
                ans += val[i][j];
                link(s, id[i][j], val[i][j]);
                link(id[i][j], cnt + i, inf);
                link(id[i][j], cnt + j, inf);
            }
            else {
                link(id[i][j], t, -val[i][j]);
                link(id[i][j], cnt + i, inf);
                link(id[i][j], cnt + j, inf);
            }
            if(i != j) {
                link(id[i][j], id[i + 1][j], inf);
                link(id[i][j], id[i][j - 1], inf);
            }
        }
}
    
int main() {
    n = in(), m = in();
    for(int i = 1; i <= n; i++) a[i] = in();
    for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j++)
            val[i][j] = in();
    build();
    printf("%d", ans - dinic());
    return 0;
}

思想很危險,目前還停留在noip,看到\(n\leq 100\),居然只想到了DP,而且還不會寫,網路流啊。。。