1. 程式人生 > >[LOJ 2146][BZOJ 4873][Shoi2017]壽司餐廳

[LOJ 2146][BZOJ 4873][Shoi2017]壽司餐廳

sizeof ffffff pty \n 一起 每天 null () .com

[LOJ 2146][BZOJ 4873][Shoi2017]壽司餐廳

題意

比較復雜放LOJ題面好了qaq...

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

\(n\le 100,a_i\le 1000,m\in\{0,1\}\)

題解

好像是個...裸的最大權閉合子圖...最大權閉合子圖都不知道果然是學了假的網絡流

顯然選了一個區間後所有子區間也要選, 選了一個 \(d_{i,i}\) 後就要選花費點. 花費點可以拆成兩部分, 一部分是 \(cx\) 直接從 \(d_{i,i}\) 裏減去就可以了, 另一部分是 \(mx^2\) 需要對於每一個壽司代號都新建一個點來表達.

然後它們之間的制約關系表達成有向邊, 求最大權閉合子圖就可以了.

果然我還是太菜了...

參考代碼

#include <bits/stdc++.h>

const int MAXN=1e2+10;
const int MAXV=1e4+10;
const int MAXE=1e6+10;
const int INF=0x7FFFFFFF;

struct Edge{
    int from;
    int to;
    int flow;
    Edge* rev;
    Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* cur[MAXV];
Edge* top=E;

int n;
int m;
int a[MAXN];
bool vis[1010];
int depth[MAXV];
int id[MAXN][MAXN];
int val[MAXN][MAXN];

bool BFS(int,int);
int Dinic(int,int);
int DFS(int,int,int);
void Insert(int,int,int);

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",a+i);
    int s=0,t=1;
    int cnt=1,sum=0;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            scanf("%d",val[i]+j);
            id[i][j]=++cnt;
            if(i==j)
                val[i][j]-=a[i];
            if(val[i][j]>=0){
                sum+=val[i][j];
                Insert(s,id[i][j],val[i][j]);
            }
            else
                Insert(id[i][j],t,-val[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            Insert(id[i][j],id[i+1][j],INF);
            Insert(id[i][j],id[i][j-1],INF);
        }
    }
    for(int i=1;i<=n;i++)
        Insert(id[i][i],cnt+a[i],INF);
    for(int i=1;i<=n;i++){
        if(!vis[a[i]]){
            vis[a[i]]=true;
            Insert(cnt+a[i],t,m*a[i]*a[i]);
        }
    }
    printf("%d\n",sum-Dinic(s,t));
    return 0;
}

int Dinic(int s,int t){
    int ans=0;
    while(BFS(s,t))
        ans+=DFS(s,INF,t);
    return ans;
}

bool BFS(int s,int t){
    memset(depth,0,sizeof(depth));
    std::queue<int> q;
    q.push(s);
    depth[s]=1;
    cur[s]=head[s];
    while(!q.empty()){
        s=q.front();
        q.pop();
        for(Edge* i=head[s];i!=NULL;i=i->next){
            if(i->flow>0&&depth[i->to]==0){
                depth[i->to]=depth[s]+1;
                cur[i->to]=head[i->to];
                if(i->to==t)
                    return true;
                q.push(i->to);
            }
        }
    }
    return false;
}

int DFS(int s,int flow,int t){
    if(s==t||flow<=0)
        return flow;
    int rest=flow;
    for(Edge*& i=cur[s];i!=NULL;i=i->next){
        if(i->flow>0&&depth[i->to]==depth[s]+1){
            int tmp=DFS(i->to,std::min(rest,i->flow),t);
            if(tmp<=0)
                depth[i->to]=0;
            rest-=tmp;
            i->flow-=tmp;
            i->rev->flow+=tmp;
            if(rest<=0)
                break;
        }
    }
    return flow-rest;
}

inline void Insert(int from,int to,int flow){
    top->from=from;
    top->to=to;
    top->flow=flow;
    top->rev=top+1;
    top->next=head[from];
    head[from]=top++;

    top->from=to;
    top->to=from;
    top->flow=0;
    top->rev=top-1;
    top->next=head[to];
    head[to]=top++;
}

技術分享圖片

[LOJ 2146][BZOJ 4873][Shoi2017]壽司餐廳